Files
rust_leptos/AI.md
2026-04-12 15:05:31 +02:00

4.5 KiB

AI Guidelines — Domotique Rust/Leptos

Project overview

Home automation dashboard built with Leptos (full-stack Rust). Exposes a web UI for managing quick links and controlling shutters via HTTP API to IoT devices. Runs as a systemd service with SSR + WASM hydration.

Tech stack

Layer Technology
Framework Leptos 0.6 (SSR + hydration)
Server Axum 0.7 + Tokio
Database MySQL via SQLx 0.8 (compile-time checked queries)
Frontend WASM (wasm-bindgen), Tailwind CSS 4
Build cargo-leptos, Makefile

Architecture

Dual-target compilation

  • ssr feature: binary target, runs Axum, has DB access, pre-renders HTML
  • hydrate feature: lib target compiled to WASM, mounts into DOM

Gate all server-only code (database, filesystem, secrets) with #[cfg(feature = "ssr")].

Directory layout

src/
  app.rs          # App component, router, nav
  lib.rs          # WASM entry point (hydrate feature)
  main.rs         # Binary entry point (ssr feature)
  setup.rs        # Axum server initialization
  database.rs     # Global MySqlPool (OnceLock)
  models/         # Data models + SQL queries
  routes/         # Page components + server functions
migrations/       # SQLx migrations (timestamp-named)
style/            # Compiled Tailwind output
input.css         # Tailwind source

Data flow

  1. Page component defines a Resource that calls a server function
  2. Server function (#[server]) queries the DB via sqlx::query!
  3. Mutations use create_server_action or create_action
  4. After mutation, increment a version signal to trigger resource refetch

Coding conventions

Language & naming

  • UI labels and user-facing strings are in French (e.g., Liens, Volets)
  • Rust identifiers follow standard conventions: snake_case for functions/variables, PascalCase for types and components
  • Server function names are descriptive PascalCase (e.g., GetLinksAction, LinkAction)

Components

  • One #[component] per logical unit, small and single-responsibility
  • Use create_rw_signal / create_signal for local state
  • Use Resource for async data; include a version signal for cache invalidation
  • Modal pattern for forms: single component toggled between create/edit mode

Server functions

#[server(MyAction, "/api", "GetJson")]  // or "Cbor" for mutations
pub async fn my_action(...) -> Result<T, ServerFnError> {
    // DB access gated by ssr feature implicitly via #[server]
}
  • Return Result<T, ServerFnError>
  • Log errors with tracing::error! before returning a generic error to the client
  • Never expose internal error details to the client

Database

  • Use sqlx::query! / sqlx::query_as! for compile-time checked SQL
  • Access the pool via the global get_db() helper in database.rs
  • Keep queries in the model file for the relevant type (models/link.rs)
  • Connection pool max: 4 connections — avoid holding connections across await points

Styling

  • Tailwind utility classes only — no custom CSS except CSS variables in input.css
  • Dark-first design; use dark: variants when needed
  • Custom color tokens (defined in input.css, OKLCH color space):
    • prim, prim-light — primary accent
    • second, second-dark — secondary
    • third, third-light — tertiary accent
    • fourth, green — utility
  • Responsive breakpoints: xs:, sm:, md:, lg:

Error handling

  • Server functions: Result<T, ServerFnError>, log then return generic message
  • Components: handle None / error states explicitly in the view
  • Avoid unwrap() in production paths; use ? or explicit error handling

Development workflow

# Start dev server (auto-reload)
cargo leptos watch

# Watch Tailwind in parallel
npx tailwindcss -i input.css -o ./style/output.css --watch

# Run DB migrations
sqlx migrate run

# Production deploy
make install   # builds Tailwind + WASM + binary, restarts systemd service

Environment variables are loaded from .env (DATABASE_URL required).

Key patterns to follow

  • No new dependencies without a clear reason — the stack is intentionally minimal
  • No JavaScript — all interactivity through Leptos/WASM
  • Keep components in routes/ unless genuinely reusable across multiple pages
  • New DB tables require a migration file in migrations/
  • External API calls (e.g., shutters) go through server functions, never from WASM directly
  • Do not add features beyond what is asked; this is a personal tool, not a framework