Files
Brandon Thomas 0004726a36 ArtCraft Desktop (fresh start)
This is a fresh start for the `artcraft` repo.

The old monorepo, which included our web API gateway, job services, SRE
tools, various frontends, and more has moved to `artcraft-services`

You can find the full git history here:

https://github.com/storytold/artcraft-services

This change was motivated by the fact that the entire git history was
hundreds of gigabytes in size, which is unwieldy for anyone not in the
core team that doesn't care about support services outside of the core
artcraft app.

We wanted to keep this repo in its well-known place since we already
have users and activity here.

Please update your remotes to either `artcraft-services` or make a fresh
clone of this repo.

History can be found at the artcraft-services repo.
2026-09-11 14:19:51 -04:00

4.9 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: 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