Presets and markers¶
A preset is a whole project template. A marker is a comment pair
inside a generated file marking the region the scaffolder owns. Together
they are what makes rtb regenerate project able to refresh a tool
without destroying what you added to it.
Bundled presets¶
Two ship in the binary. --preset selects one; an unknown name is a hard
error, never a silent fall back to the default.
| Preset | Default | Depends on | Command shape |
|---|---|---|---|
minimal |
yes | clap, linkme only |
A local Command trait with name / about / clap / run, dispatched from a linkme slice |
cli |
no | the rust-tool-base umbrella crate, plus tokio, miette, async-trait, clap, linkme, serde, schemars |
The framework Command trait — spec() and async run(App) — with arguments in a #[derive(Parser)] struct re-parsed through rtb::cli::parse_passthrough |
Both render the same five files: Cargo.toml, .gitignore,
README.md, src/main.rs, src/commands/mod.rs. generate project
also writes .rtb/manifest.yaml, for six files in total.
Why minimal depends on nothing from the framework¶
minimal deliberately pulls in only clap and linkme, so
cargo check on a freshly scaffolded tool works without reaching the
network for the framework crates. That makes it the preset to reach for
when you want a working binary immediately, or when you are demonstrating
the scaffolder somewhere hermetic. Switch to cli when you want the
built-in command suite.
What cli gives you that minimal does not¶
The cli preset's main.rs calls rtb_cli::Application::builder(), so
the tool inherits version, doctor, update, docs, mcp, config,
credentials and telemetry — see built-in
commands. It also installs a typed
AppConfig through rtb::config::Config, which is what makes
config schema and config validate meaningful in a scaffolded tool.
The framework version pin is rendered, not hardcoded¶
The cli preset's Cargo.toml pins
rtb = { package = "rust-tool-base", version = "{{ rtb_version }}" },
where the value is the umbrella version the scaffolding rtb binary was
built from. A template must never hardcode that pin; a guard test in the
workspace fails the build if one does.
preset_version¶
The manifest records a preset_version — an internal schema counter
bumped whenever the rendered shape changes in a way the regenerator has
to notice. Despite the name of the constant behind it
(MINIMAL_PRESET_VERSION) there is one counter, written for both
presets. It is at 5:
| Version | Change |
|---|---|
| 1 | Single-file clap binary with --version. |
| 2 | commands/ module with linkme-based dispatch, so generate command no longer edits main.rs. |
| 3 | AppConfig stub with settings markers, so generate setting has somewhere to write. |
| 4 | --version gained help text and stopped being global, so it no longer appeared blank on every subcommand. |
| 5 | The cli preset's main.rs moved from VersionInfo::from_env() to version_info!(). |
Version 5 matters beyond tidiness: a tool scaffolded at version 4 reports
the framework's version rather than its own, and rtb update refuses to
swap a binary whose reported version does not match the release tag.
Regenerating moves it onto the macro.
Marker regions¶
| Marker pair | File | Preset | Owns |
|---|---|---|---|
// rtb:commands-begin / -end |
src/commands/mod.rs |
both | pub mod <command>; lines, kept sorted and deduplicated |
// rtb:flags-begin / -end |
src/commands/<cmd>.rs |
minimal | .arg(clap::Arg::new(…)) builder calls |
// rtb:args-begin / -end |
src/commands/<cmd>.rs |
cli | #[derive(Parser)] struct fields |
// rtb:settings-begin / -end |
src/main.rs |
both | AppConfig fields |
Regeneration splices the interior of each region from the file on disk into the freshly rendered template. Everything outside the markers is taken from the new template. Leading whitespace before a marker is tolerated, which is what lets the settings markers sit indented inside a struct body.
Removing or reordering a marker line breaks the splice for that region: the fresh template's empty region wins and your additions are gone. If you need to move code out of a marked region, move it to a file the manifest does not own.
Templates are embedded, and escaped on the way out¶
Templates live under crates/rtb-cli-bin/templates/ and are compiled
into the binary with rust-embed, so scaffolding works with no network
access. They render through minijinja, and directories starting with
_ — currently just _partials/ — are internal rather than selectable
presets.
Four context-aware filters are registered on the template environment and
applied at non-code render sites: escape_yaml, escape_toml,
escape_markdown, escape_shell_arg. Each is the identity function on
the safe character class, never panics, and cannot fail. They are
defence in depth — the primary defence is
input validation, which rejects the dangerous
character classes before a value reaches a template at all.
Related¶
.rtb/manifest.yaml— wherepresetandpreset_versionare recorded.rtb regenerate— what marker splicing protects you from.