# MNW Code Patterns Recurring patterns, macros, and conventions used throughout the MNW codebase. ## Macros ### `impl_str_enum!` **Location:** `src/db/enums.rs` Generates `Display`, `FromStr`, and sqlx `Type`/`Encode`/`Decode` for enums that map to text columns. The sqlx impls delegate to `String`, so the enum works with both TEXT and VARCHAR columns. ```rust #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum CodePurpose { Discount, FreeAccess, FreeTrial, } impl_str_enum!(CodePurpose { Discount => "discount", FreeAccess => "free_access", FreeTrial => "free_trial", }); ``` **What it generates:** - `Display::fmt()` writes the mapped string literal - `FromStr::from_str()` parses back, with error `"invalid CodePurpose: xyz"` - `sqlx::Type`/`Encode`/`Decode` so sqlx can read/write the enum directly in queries **Used for:** `DiscountType`, `CodePurpose`, `WaitlistStatus`, `TransactionStatus`, `ItemType`, `FileScanStatus`, `SelectionMethod`, `LoginTokenPurpose`, and more. ### `define_pg_uuid_id!` **Location:** `src/db/id_types.rs` Creates newtype wrappers around `uuid::Uuid` for type-safe IDs. Prevents accidental mixing of ID types at compile time (e.g., `UserId` vs `ProjectId`), while remaining transparent in the database layer and JSON APIs. ```rust define_pg_uuid_id!( UserId, ProjectId, ItemId, VersionId, TransactionId, // ... 30+ more ); ``` **What each type gets:** - `Copy`, `Clone`, `Hash`, `Eq`, `Ord` - `::new()` generates a UUID v4 - `::from_uuid(uuid)` and `From` - `.as_uuid()` and `Deref` - `::nil()` for test fixtures - `serde(transparent)` -- serializes as a UUID string - `sqlx::Type`/`Encode`/`Decode` for PostgreSQL UUID columns - `Display` and `FromStr` ### `impl_into_response!` **Location:** `src/templates/mod.rs` Bulk-implements `IntoResponse` for Askama template structs, so they can be returned directly from route handlers. ```rust impl_into_response!( IndexTemplate, LoginTemplate, DashboardUserTemplate, // ... 90+ template types ); ``` Each implementation calls `render_template(self)`, which renders to HTML and returns a 200 response (or a 500 error page if rendering fails). ## HTMX Dual-Response Pattern See `CONTRIBUTING.md` § HTMX Responses for the full pattern (dual-format handlers, response table, helper functions). ## `ListResponse` Envelope **Location:** `src/types/mod.rs` All JSON list endpoints wrap results in a `{ "data": [...] }` envelope, making the response forward-compatible for pagination metadata. ```rust #[derive(Serialize)] pub struct ListResponse { pub data: Vec, } ``` Usage: ```rust let chapters = db::chapters::get_chapters_by_item(&state.db, item_id).await?; Ok(Json(ListResponse { data: chapters.into_iter().map(ChapterResponse::from).collect() })) ``` ## Error Handling See `CONTRIBUTING.md` § Error Handling for the `AppError` enum, HTTP status mapping, user-safe message rules, and JSON error middleware. Source: `src/error.rs`. ## Rate Limiting **Location:** `src/helpers.rs` (config builders), `src/routes/api/mod.rs` (applied to routes) Uses `tower-governor` with `SmartIpKeyExtractor` (reads X-Forwarded-For or direct IP). ```rust let write_rate_limit = rate_limiter_ms( constants::API_WRITE_RATE_LIMIT_MS, constants::API_WRITE_RATE_LIMIT_BURST, ); let write_routes = Router::new() .route("/api/projects", post(projects::create_project)) // ... .route_layer(GovernorLayer { config: write_rate_limit }); ``` Rate limits are grouped by tier: - **Write routes** -- mutations (create, update, delete) - **Export routes** -- data export endpoints (lower burst) - **License key routes** -- activation/validation (tight limits) - **Dashboard GET routes** -- read-heavy dashboard endpoints ## Template View Models **Location:** `src/templates/` (public.rs, dashboard.rs, partials.rs) Each template is an Askama struct with all the data it needs pre-loaded. No database calls happen in templates. ```rust #[derive(Template)] #[template(path = "pages/project.html")] pub struct ProjectTemplate { pub csrf_token: CsrfTokenOption, pub session_user: Option, pub project: Project, pub creator_username: String, pub items: Vec, pub subscription_tiers: Vec, pub is_following: bool, pub follower_count: i64, // ... } ``` ### Organization | Module | Contents | |--------|----------| | `templates/public.rs` | Public pages (landing, auth, profiles, content) | | `templates/dashboard.rs` | Creator dashboards, admin views | | `templates/partials.rs` | HTMX fragments, tab content, alerts, form status | ### Physical template files ``` templates/ base.html Base layout with head, nav, footer pages/ Full public pages (30+) dashboards/ Creator + admin dashboards partials/ HTMX fragments tabs/ Tab content for dashboards wizards/ Multi-step flows (join wizard) steps/ ``` ### Common partial types - `AlertTemplate` -- feedback messages with optional link (builder pattern) - `FormStatusTemplate` -- inline success/error for HTMX form submissions - Tab templates -- one struct per dashboard tab, loaded via HTMX on tab click ## Route Organization Routes are split by audience: ``` routes/ api/ JSON endpoints (grouped by domain: items, projects, subscriptions, etc.) pages/ public/ Public-facing HTML pages dashboard/ Authenticated creator dashboard pages feeds.rs RSS/Atom feeds blog.rs Blog pages auth.rs Login, signup, logout, password reset git/ Source browser (directory module) git_issues/ Issue tracker (directory module) oauth.rs OAuth provider (for Multithreaded) postmark/ Inbound email webhook (directory module) storage/ File upload/download (presigned URLs, directory module) stripe/ Stripe webhooks and Connect callbacks synckit.rs SyncKit cloud sync + OTA API ``` The `api/mod.rs` file composes all API sub-routers, applies rate limiting layers, and adds the JSON error middleware. ## Database Conventions - All tables use UUID primary keys (except `sync_log` which uses BIGSERIAL) - Text enums stored as VARCHAR/TEXT, mapped via `impl_str_enum!` - Timestamps are `TIMESTAMPTZ NOT NULL DEFAULT NOW()` unless nullable - Foreign keys use `ON DELETE CASCADE` for owned relationships - Compile-time checked queries via sqlx -- migrations auto-run on boot - Each integration test creates and drops its own PostgreSQL database ## Key Paths | Pattern | Location | |---------|----------| | Enum macro | `src/db/enums.rs` | | ID macro | `src/db/id_types.rs` | | Template macro | `src/templates/mod.rs` | | Error type | `src/error.rs` | | HTMX helpers | `src/helpers.rs` | | Rate limiters | `src/helpers.rs`, `src/routes/api/mod.rs` | | ListResponse | `src/types/mod.rs` | | Constants | `src/constants.rs` |