Part of #9. Phase 0 — foundations.
Learning goals
Packages/crates/modules and visibility (TRPL Managing Growing Projects with Packages, Crates, and
Modules), error handling design (Result, ?, a crate-level error enum with thiserror —
TRPL Error Handling), clap derive, workspace layout, tracing for logs.
Task
Turn the flat scaffold into the modular package every CLDK analyzer shares (mirror
codeanalyzer-python's shape in Rust idiom — one module per phase, seams first):
src/
main.rs thin: parse CLI -> options -> core::analyze -> emit
cli.rs clap derive, the FULL CLDK flag contract
core.rs ORCHESTRATOR ONLY — delegates every phase, inlines nothing
options.rs AnalysisOptions
config.rs extensions, skip-dirs, cache subdir name
schema/ the entities/ types, evolved in issue 02
syntactic_analysis/ symbol-table builder (issues 04–05)
semantic_analysis/ call graph (06) + mir/ stub subdir (the level-2 seam, empty)
analysis/ pluggable pass layer: AnalysisPass trait + registry (empty but wired)
frameworks/ entrypoint-finder trait (empty but wired)
utils/ logging, progress — no analysis logic
CLI: align to the CLDK contract — -i/--input, -o/--output (stdout JSON when omitted),
-f/--format json|msgpack, --emit json|neo4j|schema, -a 1|2 (3 arrives in issue 16),
-t/--target-files, --skip-tests/--include-tests, --eager/--lazy, -c/--cache-dir,
-j/--jobs, -v (count). Remove the -f/--project-root-path flag (field project_root_pom) — a fossil copied from codeanalyzer-java, where it points at the root pom.xml/build.gradle for classpath materialization. Rust needs none of it: cargo metadata from -i finds the workspace-root Cargo.toml itself, and -f is needed for --format. (--build-cmd/--no-build/--no-clean-dependencies are the same Java inheritance — drop them too; revisit --manifest-path only if a real workspace edge case demands it.) Decide the binary
name (canrs fits the canpy/cants/canclang convention) and record it + the tooling table in the
README under Architecture & Tooling.
Teacher's notes
- The empty-but-wired seams are the deliverable, not ceremony: the TS analyzer shipped without
them and could not host extensions without a rewrite (references/analyzer-architecture.md). core::analyze() should read as a table of contents. If a match arm grows analysis logic, it's
in the wrong file.- An
AnalysisPass trait with requires()/provides() is your first real trait design. Keep it
object-safe (Box<dyn AnalysisPass>) — you'll learn why that constraint exists.
Gate
cargo build && cargo test && cargo clippy -- -D warnings green.canrs --help shows the contract; --format msgpack exits non-zero with a clear message
(never a silent fallback).- Every skeleton module exists and
core compiles against the (still-stub) phase functions.
Part of #9. Phase 0 — foundations.
Learning goals
Packages/crates/modules and visibility (TRPL Managing Growing Projects with Packages, Crates, and
Modules), error handling design (
Result,?, a crate-level error enum withthiserror—TRPL Error Handling), clap derive, workspace layout,
tracingfor logs.Task
Turn the flat scaffold into the modular package every CLDK analyzer shares (mirror
codeanalyzer-python's shape in Rust idiom — one module per phase, seams first):CLI: align to the CLDK contract —
-i/--input,-o/--output(stdout JSON when omitted),-f/--format json|msgpack,--emit json|neo4j|schema,-a 1|2(3 arrives in issue 16),-t/--target-files,--skip-tests/--include-tests,--eager/--lazy,-c/--cache-dir,-j/--jobs,-v(count). Remove the-f/--project-root-pathflag (fieldproject_root_pom) — a fossil copied from codeanalyzer-java, where it points at the root pom.xml/build.gradle for classpath materialization. Rust needs none of it:cargo metadatafrom-ifinds the workspace-root Cargo.toml itself, and-fis needed for--format. (--build-cmd/--no-build/--no-clean-dependenciesare the same Java inheritance — drop them too; revisit--manifest-pathonly if a real workspace edge case demands it.) Decide the binaryname (
canrsfits the canpy/cants/canclang convention) and record it + the tooling table in theREADME under Architecture & Tooling.
Teacher's notes
them and could not host extensions without a rewrite (
references/analyzer-architecture.md).core::analyze()should read as a table of contents. If a match arm grows analysis logic, it'sin the wrong file.
AnalysisPasstrait withrequires()/provides()is your first real trait design. Keep itobject-safe (
Box<dyn AnalysisPass>) — you'll learn why that constraint exists.Gate
cargo build && cargo test && cargo clippy -- -D warningsgreen.canrs --helpshows the contract;--format msgpackexits non-zero with a clear message(never a silent fallback).
corecompiles against the (still-stub) phase functions.