Spec-Driven

Spec-Driven Rust: Structure

three library crates, one binary, one Cargo workspace

Synopsis:
This page covers Rust_TextFinder_Structure.md, which fixes the components, the dependency direction, and the build.
  • Three library crates and one binary crate, matching the C++ division component for component. Only the spelling changes - snake_case where C++ uses camelCase.
  • Each library is its own package rather than a module of one crate, so Cargo enforces the dependency chain and a call in the wrong direction fails to build.
The document declares one interface, the seam between traversal and the sink.
  • An Output trait bound through a generic parameter, so dispatch is static and the bound is checked at compile time.
  • The receiver is &mut self, and that one word puts a unique borrow rather than a shared one into the Dirnav constructor.
  • The rejected Box<dyn Output> is recorded as viable and not adopted - the same choice C++ answers the same way one layer down.
One workspace, four members, and one third-party crate in the whole project.
  • Only rust_textfinder_dirnav declares regex; the binary declares no third-party crate at all.
  • Nothing in this build is experimental or version-fragile, which is worth saying only because the C++ build is the other case.
  • One requirement of the parent specification is met by doing nothing: Rust writes the bytes it is given, so no stream mode is set.

1.  Three Libraries and One Binary

Rust_TextFinder_Structure.md fixes the components, the dependency direction, and the build. It is one of the two file patterns Constitution.md rule 1 names as authoritative for code, which is what lets it declare a trait rather than only describe a layout.
Component Kind Responsibility
rust_textfinder_cmdline library crate Parses a slice of argument strings into ProgramCommands. Opens no stream, touches no filesystem
rust_textfinder_dirnav library crate Walks, reads, matches, formats. The only component that touches file contents. Compiles the expression once per run
rust_textfinder_output library crate Writes each string to stdout as one line. Absorbs every write failure
rust_textfinder_entry binary crate Produces the executable rust_textfinder. Owns the skip list and wires the three together
Each library's Cargo package name matches its component name. The binary package is rust_textfinder_entry and the executable it produces is rust_textfinder - two names for two things, which Cargo expresses as a [[bin]] section naming the target rather than letting it default to the package name. The division matches the boundaries the Behavior thread uses, and matches the C++ division component for component. Identifiers follow Rust convention, so this implementation spells in snake_case what the C++ implementation spells in camelCase: usage_line for usageLine, line_numbers for lineNumbers. The switch letters, the emitted text, and the exit codes come from Spec_TextFinder.md and are identical in both.

2.  Dependency Direction

The binary depends on all three libraries. The libraries themselves form a chain rather than a star.
rust_textfinder_cmdline
        ^
        | depends on (ProgramCommands)
        |
rust_textfinder_dirnav
        ^
        | depends on (the Output trait)
        |
rust_textfinder_output
        ^
        | depends on (all three)
        |
rust_textfinder_entry  --->  rust_textfinder.exe
rust_textfinder_dirnav depends on rust_textfinder_cmdline for ProgramCommands, and rust_textfinder_output depends on rust_textfinder_dirnav for the Output trait it implements. Nothing depends on rust_textfinder_output but the binary, which supplies it as the generic argument that binds Dirnav to a concrete sink. Each library is a separate package rather than a module of one crate, and the structure document says why: Cargo enforces the chain above, so a call in the wrong direction fails to build rather than passing review. Four Cargo.toml files state the four dependency sets, and a fifth at the root lists the members. The direction is what keeps rust_textfinder_dirnav testable without a real sink. Its unit suite supplies its own Recorder, a struct of one Vec<String> that implements Output, which satisfies the same bound the real sink does and also demonstrates that the generic parameter binds to any implementation of the trait.

3.  The Output Trait and the Generic Parameter

The structure document declares one interface, and it is the seam between traversal and the sink:
pub trait Output {
    fn output(&mut self, text: &str);
}
Dirnav binds to a concrete Output through a generic type parameter rather than through a trait object, so the call is statically dispatched and the bound is checked at compile time. The receiver is &mut self, because emitting a line mutates the sink, and that one word is what puts a unique borrow rather than a shared one into the Dirnav constructor. The parameter is &str rather than String. The sink neither stores what it is handed nor modifies it, so ownership would be transferred only to be dropped, and rust_textfinder_dirnav can pass a borrowed path line without allocating. The rejected alternative is recorded in the document rather than forgotten: a Box<dyn Output> trades static dispatch for a Dirnav signature carrying no type parameter, and is named as viable and not adopted. The C++ implementation faces the same choice one layer down and answers it the same way, with a concept constraint naming the base class and a template parameter making the call direct. A structure document that records the road not taken tells the next reader that the choice was a choice.

4.  Cargo, the Workspace, and the One Dependency

  • Rust edition 2021, Cargo. The structure document holds the edition and the toolchain floor once, and each component's Build section reads "Per Rust_TextFinder_Structure.md" and names only its own package.
  • One workspace at the root of the Rust folder with four members, one per component, sharing one Cargo.lock and one target/ directory.
  • Toolchain minimum: rustc 1.70 with the matching Cargo, declared as rust-version in all four manifests. No nightly feature is used.
One third-party crate appears in the whole project. Spec_TextFinder.md §6.1 assigns the regex crate to Rust, §6 permits a package from the language's supported ecosystem for regex access, and only rust_textfinder_dirnav declares it. Everything else comes from the standard library. The binary declares no third-party crate at all, so the dependency reaches it through one package rather than four. Building is two lines from the Rust project directory, and the workspace flag is what makes the first of them build all four packages rather than the one the current directory names.
cargo build --workspace
target\debug\rust_textfinder.exe -H true
Nothing in this build is experimental, and nothing about it is version-fragile. That is worth one sentence only because the C++ build is the other case: it gates import std; behind an opt-in token that CMake neither prints nor documents. The two projects implement one specification and their build sections have almost nothing in common. One requirement of Spec_TextFinder.md §3.4 this implementation meets by doing nothing. §3.4 obliges an implementation to stop its runtime translating the LF terminator to CRLF; Rust's standard output writes the bytes it is given on every platform, so no stream mode is set, and no #[cfg] on the target platform appears in rust_textfinder_output. The Output page covers what follows from that.

5.  Source

The structure document in full, then the workspace manifest and the four package manifests. The document is 40 lines and fixes everything above; the manifests add nothing to it beyond the paths.
Rust_TextFinder_Structure.md
# Rust_TextFinder — Project Structure

The Rust_TextFinder project comprises three libraries and one binary, arranged as a single Cargo workspace. The binary depends on all three. The libraries themselves form a chain: `rust_textfinder_dirnav` depends on `rust_textfinder_cmdline` for the program-command struct, and `rust_textfinder_output` depends on `rust_textfinder_dirnav` for the `Output` trait. Nothing depends on `rust_textfinder_output` but the binary.

## Libraries

- **rust_textfinder_cmdline** — parses the command line into a `struct` of program commands that control the behavior of `rust_textfinder_dirnav`. Specified in [Spec_Rust_TextFinder_Cmdline.md](Rust_Spec_driven_Cmdline/Spec_Rust_TextFinder_Cmdline.md).
- **rust_textfinder_dirnav** — directory navigation. Reads file contents, runs regex matching, and formats each matching file into the block Spec_TextFinder.md §3.4 fixes before emitting its lines. Compiles the regular expression once per run, not once per file. Defines the trait:
  ```rust
  pub trait Output {
      fn output(&mut self, text: &str);
  }
  ```
  `Dirnav` binds to a concrete `Output` through a generic type parameter, so the call is statically dispatched. Specified in [Spec_Rust_TextFinder_Dirnav.md](Rust_Spec_driven_Dirnav/Spec_Rust_TextFinder_Dirnav.md).
- **rust_textfinder_output** — implements `Output::output` according to its specification, [Spec_Rust_TextFinder_Output.md](Rust_Spec_driven_Output/Spec_Rust_TextFinder_Output.md). Handles output errors internally.

Each library's Cargo package name matches its component name above, and each is a library crate exporting the items its specification fixes.

## Binary

- **rust_textfinder_entry** — binary package name; produces the executable `rust_textfinder`. Depends on the three libraries above. Specified in [Spec_Rust_TextFinder_Entry.md](Rust_Spec_driven_TextFinder_Entry/Spec_Rust_TextFinder_Entry.md).
- Owns the skip list and passes it to `rust_textfinder_dirnav` for use during traversal.
- On execution, the binary command line is parsed into a program-command struct using `rust_textfinder_cmdline`.
- A value of the concrete output type is created and bound to a `Dirnav` value through its generic parameter.
- The `Dirnav` value is started at each of the specified (possibly default) root paths in turn and performs a DFS for regex matches on files in each directory tree.
- Owns the process's one output value, which in turn owns the only handle to stdout. Every write to stdout, the binary's own help text and option listing included, passes through that value, so nothing else can interleave with the search output or reorder it.

## Build

- Language: Rust, edition 2021. Build system: Cargo. These apply to every package below, and each component's `Spec_*.md` names only its own package.
- One workspace at the root of this folder with four members, one per component. Each library is a separate package rather than a module of one crate, so the dependency chain above is enforced by Cargo rather than by convention.
- Dependencies: the `regex` crate, which Spec_TextFinder.md §6.1 assigns to Rust, is the only third-party dependency, and only `rust_textfinder_dirnav` declares it. Everything else comes from the standard library. Spec_TextFinder.md §6 permits a package from the language's supported ecosystem for regex access.
- Toolchain minimum: rustc 1.70 with the matching Cargo. No nightly feature is used.

## Notes

- This forms a data pipeline architecture that emits an output immediately following evaluation of a regex match.
- Identifiers follow Rust convention, so this implementation spells in snake_case what the C++ implementation spells in camelCase: `usage_line` for `usageLine`, `line_numbers` for `lineNumbers`. The switch letters, the emitted text, and the exit codes are fixed by Spec_TextFinder.md and are identical in both.
- The `Output` trait with a generic parameter is the current design choice. A boxed trait object, `Box<dyn Output>`, is a viable alternative that trades static dispatch for a signature carrying no type parameter; it is not adopted here.
- Spec_TextFinder.md §3.4 obliges an implementation to stop its runtime translating the LF terminator. Rust's standard output performs no such translation on any platform, so this implementation meets that requirement without configuring the stream. [Spec_Rust_TextFinder_Output.md](Rust_Spec_driven_Output/Spec_Rust_TextFinder_Output.md) §5 records what follows from that.
Cargo.toml (workspace root)
[workspace]
resolver = "2"
members = [
    "Rust_Spec_driven_Cmdline",
    "Rust_Spec_driven_Dirnav",
    "Rust_Spec_driven_Output",
    "Rust_Spec_driven_TextFinder_Entry",
]
Rust_Spec_driven_Cmdline/Cargo.toml
[package]
name = "rust_textfinder_cmdline"
version = "0.1.0"
edition = "2021"
rust-version = "1.70"

[lib]
path = "src/lib.rs"

[dependencies]
Rust_Spec_driven_Dirnav/Cargo.toml
[package]
name = "rust_textfinder_dirnav"
version = "0.1.0"
edition = "2021"
rust-version = "1.70"

[lib]
path = "src/lib.rs"

[dependencies]
regex = "1"
rust_textfinder_cmdline = { path = "../Rust_Spec_driven_Cmdline" }
Rust_Spec_driven_Output/Cargo.toml
[package]
name = "rust_textfinder_output"
version = "0.1.0"
edition = "2021"
rust-version = "1.70"

[lib]
path = "src/lib.rs"

[dependencies]
rust_textfinder_dirnav = { path = "../Rust_Spec_driven_Dirnav" }
Rust_Spec_driven_TextFinder_Entry/Cargo.toml
[package]
name = "rust_textfinder_entry"
version = "0.1.0"
edition = "2021"
rust-version = "1.70"

[[bin]]
name = "rust_textfinder"
path = "src/main.rs"

[dependencies]
rust_textfinder_cmdline = { path = "../Rust_Spec_driven_Cmdline" }
rust_textfinder_dirnav = { path = "../Rust_Spec_driven_Dirnav" }
rust_textfinder_output = { path = "../Rust_Spec_driven_Output" }
The four manifests are the dependency chain of Section 2 written down. Read the [dependencies] sections in order and the chain reads back: Cmdline declares none, Dirnav declares regex and Cmdline, Output declares Dirnav, and the binary declares all three.

6.  Prompt Records

This page carries none. Page_Structure.md §8 assigns it Prompts_Rust_TextFinder_Structure.md and Prompts_Fix_Rust_TextFinder_Structure.md, and neither was written: the structure document and the four component specifications were produced in one turn, recorded at the project level, which §8's level rule sends to the Process thread. §8 lets a page stand without a block rather than requiring one, and the Process page says what the thread's evidence does and does not cover.