mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-20 17:02:53 -04:00
4.8 KiB
4.8 KiB
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 useuse serde_derive::{Serialize, Deserialize};- Never use
println!oreprintln!outside of tests; uselogcrate macros (info!,warn!,error!,debug!,trace!) instead - Use
anyhow::Result/AnyhowResultfor fallible functions in application code; new database queries should useResult<T, sqlx::Error>instead - SQLx queries: prefer
sqlx::query!andsqlx::query_as!compile-time checked macros over runtimesqlx::query()/sqlx::query_as(). The macros catch SQL typos, column mismatches, and type errors at build time. Use runtime queries only when assembling complicated dynamic/branching queries that cannot be expressed as a compile-time macro. - Enum Display/Debug: use
impl_enum_display_and_debug_using_to_str!macro, notderive_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::Displayfor 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
- Constants at the top, right after imports.
- 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.
- 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.
- Type ordering for API types: Request, then Response (success), then Error.
implblocks after their type definitions.
Ordering within an impl block
- Constructors (
new,from_*,builder) at the top. - Public methods next — these are the main entry points a reader cares about.
- 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.
- Imports
- Constants (test IDs, URLs, fixture data) — small context that tests reference
- Test functions or sub-modules grouped by category
- 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}Builderwithset_prefixed setters and abuild()method
Imports
- Group imports: std, external crates, internal workspace crates,
crate::imports - Prefer
useimports 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.rsand 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
idcolumns must beBIGINT(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