Files
artcraft/.claude/rules/code-style.md
T
2026-04-21 18:43:20 -04:00

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 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 queries: prefer sqlx::query! and sqlx::query_as! compile-time checked macros over runtime sqlx::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, 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