mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-03 00:47:53 +00:00
93 lines
4.9 KiB
Markdown
93 lines
4.9 KiB
Markdown
# Code Style
|
|
|
|
## Rust
|
|
|
|
- Two spaces for indentation (not four)
|
|
- No minimum supported Rust version; use latest stable features freely
|
|
- `#[macro_use] extern crate serde_derive;` is used in binary crates; library crates use `use serde_derive::{Serialize, Deserialize};`
|
|
- Never use `println!` or `eprintln!` outside of tests; use `log` crate macros (`info!`, `warn!`, `error!`, `debug!`, `trace!`) instead
|
|
- Use `anyhow::Result` / `AnyhowResult` for fallible functions in application code; new database queries should use `Result<T, sqlx::Error>` instead
|
|
- SQLx: MySQL queries belong in the `mysql_queries` crate. Do NOT write `sqlx::query!` /
|
|
`sqlx::query()` in application/service crates (e.g. `storyteller-web`, which has zero direct
|
|
SQLx queries) — add or reuse a function in `mysql_queries` and call it. The compile-time
|
|
`query!` / `query_as!` macro conventions for that crate are documented in
|
|
`crates/schema/database/mysql_queries/CLAUDE.md`.
|
|
- Enum Display/Debug: use `impl_enum_display_and_debug_using_to_str!` macro, not `derive_more::Display` (CLion doesn't understand it)
|
|
- Fields that are optional use the `maybe_` prefix: `maybe_creator_user_token`, `maybe_prompt_token`
|
|
- When implementing `fmt::Display` for error types, use the pattern: `write!(f, "{:?}", self)`
|
|
|
|
## File Layout (Top-to-Bottom Reading Order)
|
|
|
|
Organize code so a human reading top-to-bottom encounters the most important things first and
|
|
can drill down into details only when curious. Think of it like a newspaper: headline first,
|
|
then the story, then the fine print.
|
|
|
|
### Ordering within a file
|
|
|
|
1. **Constants** at the top, right after imports.
|
|
2. **The primary public type** (the main struct/enum the file is about) comes next.
|
|
A reader opening this file should immediately see what it defines.
|
|
3. **Supporting/subordinate types** (sub-structs, helper enums, serialization DTOs) go
|
|
below the primary type, not above it. Ask: "Would a reader need to know this type
|
|
exists before seeing the main type?" — if no, push it down.
|
|
4. **Type ordering for API types**: Request, then Response (success), then Error.
|
|
5. **`impl` blocks** after their type definitions.
|
|
|
|
### Ordering within an `impl` block
|
|
|
|
1. **Constructors** (`new`, `from_*`, `builder`) at the top.
|
|
2. **Public methods** next — these are the main entry points a reader cares about.
|
|
3. **Private helper methods** below public methods.
|
|
- Among helpers: place "meaty" helpers (substantial logic, direct continuations of the
|
|
public entry point) above "leaf" helpers (small formatters, simple lookups).
|
|
- A method that calls another method in the same file should appear *above* the method
|
|
it calls. This lets readers follow the call chain downward.
|
|
|
|
### Ordering within test modules
|
|
|
|
Put test cases first, helpers last. A reader opening a test file should immediately see
|
|
*what* is being tested before *how* the test machinery works.
|
|
|
|
1. **Imports**
|
|
2. **Constants** (test IDs, URLs, fixture data) — small context that tests reference
|
|
3. **Test functions** or **sub-modules** grouped by category
|
|
4. **Helper functions** (builders, pipeline runners, assertions)
|
|
|
|
Group related tests into sub-modules (`mod pricing_tests { ... }`) when a category has 2+
|
|
tests. Don't create a sub-module for a single test. Keep nesting to two levels max
|
|
(`mod tests { mod category { ... } }`). Shared helpers go in the parent module so
|
|
sub-modules can reach them via `use super::*`.
|
|
|
|
## Naming
|
|
|
|
- Handler functions: `{verb}_{noun}_handler` (e.g. `get_health_check_handler`, `login_handler`)
|
|
- Error enums: `{Feature}Error` (e.g. `FalWebhookError`, `HealthCheckError`)
|
|
- Request/response structs: `{Action}Request`, `{Action}Response`, `{Action}SuccessResponse`
|
|
- Path params: `{Action}PathInfo`
|
|
- Builder structs: `{Thing}Builder` with `set_` prefixed setters and a `build()` method
|
|
|
|
## Imports
|
|
|
|
- Group imports: std, external crates, internal workspace crates, `crate::` imports
|
|
- Prefer `use` imports over inline fully-qualified paths. Inline qualification is acceptable
|
|
only for genuine one-off references where adding an import would be noise, or for types
|
|
that collide with std prelude names (`Result`, `Option`, `Error`). If you reference a symbol
|
|
more than once, always import it.
|
|
- Prefer specific imports over wildcards, except in `api_doc.rs` and test modules
|
|
- When two crates export a type with the same name, alias with a suffix describing the source crate:
|
|
`use enums::...::CommonResolution as CommonResolutionEnum;`
|
|
`use artcraft_router::...::CommonResolution as CommonResolutionRouter;`
|
|
This avoids long fully-qualified paths in function signatures and bodies.
|
|
|
|
## MySQL
|
|
|
|
- MySQL tables: primary key `id` columns must be `BIGINT(20) UNSIGNED`. Do not use signed IDs.
|
|
- Always create tables with `ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin`.
|
|
|
|
## TypeScript / Frontend
|
|
|
|
- TypeScript with React, Vite, Zustand, Three.js
|
|
- Nx monorepo under `crates/frontend/`
|
|
- Two spaces for indentation
|
|
|