Files
rust_template/CLAUDE.md
T
tomatocream 6e460aca80 Add CLAUDE.md documenting cargo/nix workflows and architecture
Covers both dev paths (plain cargo vs nix develop/build/flake check),
LSP setup via the fenix toolchain's RUST_SRC_PATH, how nix/rust.nix
is shared between the package build and dev shell, where to add
unit vs integration tests, and how to use the rename script. Includes
a note to keep it updated as the template grows into real functionality.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ALEXkc7pTro7tF1WcUuKb
2026-08-11 00:22:30 +08:00

5.9 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What this is

A minimal Rust project template: a clap-based CLI (src/main.rs) backed by a library crate (src/lib.rs), with unit tests, an integration test, and a criterion benchmark already wired up. It also carries a full Nix flake (crane + fenix) so the same build/test/lint pipeline runs identically with plain cargo or with nix. The intent is to eventually turn this into a nix flake init template — see scripts/rename-project.sh.

Two ways to work in this repo

Cargo way — fastest inner loop, needs Rust installed locally (or run inside nix develop):

cargo run -- Zeke        # build + run the CLI, arg is the name to greet (default: "world")
cargo build               # debug build
cargo build --release
cargo test                 # unit test (src/lib.rs) + integration test (tests/greet.rs)
cargo bench                 # criterion benchmark (benches/greet.rs)
cargo clippy --all-targets -- --deny warnings
cargo fmt

To run a single test: cargo test greets_by_name (matches both the unit and integration test by name; scope to one with cargo test --test greet for just the integration test, or cargo test --lib for just the unit test).

Nix way — reproducible, pins the exact toolchain via fenix, no local Rust install required:

nix develop              # drops into a shell with the same toolchain/tools as CI, then use cargo as above
nix build                 # produces ./result/bin/rust_template
nix run                    # build + run the CLI
nix flake check             # runs every check below in parallel, each cached separately by crane
nix fmt                      # formats Rust (rustfmt), Nix (nixfmt), and TOML (taplo) via treefmt

nix flake check runs: clippy (deny warnings, all targets), fmt (rustfmt), doc (cargo doc, deny warnings), audit (cargo-audit against the RustSec advisory DB), nextest (unit + integration tests), and formatting (treefmt check). These are the same derivations nix develop's dev shell inherits, so a passing nix flake check is a reliable signal before pushing.

Both paths are meant to converge on the same result — the Nix devShell and the Nix package build are built from one shared toolchain/build-args definition (see Architecture below), so what compiles under nix build should also compile under plain cargo build inside nix develop.

IDE / LSP support

rust-analyzer is included in the fenix toolchain (nix/rust.nix), and nix develop's shellHook exports RUST_SRC_PATH so rust-analyzer can resolve std-library sources. Point your editor's rust-analyzer at the toolchain from inside nix develop (e.g. via direnv + use flake, or by launching your editor from within nix develop) rather than relying on a system-wide Rust install, so the LSP sees the same compiler version as the build.

Architecture

  • nix/rust.nix is the single source of truth for the Rust build environment: the fenix toolchain, craneLib, commonArgs (source filtering, strictDeps, native/build inputs), and the cached cargoArtifacts (deps built once via craneLib.buildDepsOnly, then reused by the package build, clippy, doc, and nextest checks — this is what makes nix build incremental instead of recompiling all dependencies on every source change). packages.default and devShells.default both derive from this file's output, which is why they stay close to identical.
  • nix/devshell.nix builds the dev shell from that same craneLib/toolchain via craneLib.devShell, adding dev-only tools (cargo-nextest, cargo-criterion, cargo-audit, cargo-expand, taplo, bacon, mold on Linux) that aren't part of the package build itself.
  • nix/treefmt.nix is a flake-parts module enabling rustfmt/nixfmt/deadnix/statix/taplo under treefmt-nix, wired into both nix fmt and the formatting check in flake.nix.
  • flake.nix ties it together with flake-parts: it imports the treefmt module, evaluates nix/rust.nix per-system, and exposes packages.default, checks, apps.default, and devShells.default from its outputs.
  • src/lib.rs holds the actual logic (currently just greet); src/main.rs is a thin clap wrapper around it. Keep new functionality in the lib crate, not main.rs, so it stays reachable from tests/ and benches/ (both depend on the rust_template lib crate by name, not on the binary).
  • tests/greet.rs is the example integration test (tests the public lib API from outside the crate); src/lib.rs's #[cfg(test)] mod tests is the example unit test (tests internals in-crate). Follow whichever pattern fits: internal/private logic → unit test in the same file; public API surface → integration test in tests/.
  • benches/greet.rs is a criterion benchmark against the lib crate; Cargo.toml disables the default libtest harness for it (harness = false) since criterion supplies its own.

Renaming the template

scripts/rename-project.sh <new_snake_case_name> replaces every rust_template occurrence (crate name in Cargo.toml, the pname/binary name in nix/rust.nix and flake.nix, and the use rust_template::... imports in src/main.rs, tests/greet.rs, benches/greet.rs), then regenerates Cargo.lock. It does not rename the project directory itself — that's a manual mv afterward (the script prints the exact command). Run nix flake check after renaming to confirm everything still resolves.

Keeping this file current

This file describes the template in its current, minimal state. As real functionality gets added (more crates, workspace layout, new checks, CI, actual product logic replacing greet), update this file to match — stale architecture notes are worse than none. Don't let it drift from what scripts/rename-project.sh actually touches, either, if the file list it edits changes.