Runtime features¶
Features is the set of built-in commands a particular invocation exposes. It is a
runtime switch: a command that is runtime-disabled is still compiled into the
binary, it is just not offered to the user.
The eleven Feature variants¶
| Variant | Gates | On by default |
|---|---|---|
Init |
the init bootstrap command |
yes |
Version |
the version command |
yes |
Update |
self-update: the update subcommand and the pre-run version check |
yes |
Docs |
the TUI documentation browser (docs) |
yes |
Mcp |
the MCP server (mcp) |
yes |
Doctor |
health-check diagnostics (doctor) |
yes |
Credentials |
the credentials list / add / remove / test / doctor subtree |
yes |
Telemetry |
opt-in anonymous telemetry | yes |
Config |
runtime config get/set (config) |
yes |
Ai |
AI-powered features — docs ask, agentic flows |
no |
Changelog |
structured release-notes display (changelog) |
no |
Nine on, two off. Feature::all() returns all eleven as a &'static [Feature], in
declaration order: Init, Version, Update, Docs, Mcp, Doctor, Ai,
Telemetry, Config, Changelog, Credentials.
Ai and Changelog are the deliberate exceptions: AI features imply an outbound
provider call and a credential, and the changelog command is not wanted by every
tool. Both are opt-in.
Turning features on and off¶
let features = Features::builder() // starts from the nine defaults
.disable(Feature::Update)
.enable(Feature::Ai)
.build();
assert!(features.is_enabled(Feature::Ai));
assert!(!features.is_enabled(Feature::Update));
assert!(features.is_enabled(Feature::Init)); // untouched defaults survive
Start from nothing instead with FeaturesBuilder::none():
| Constructor | Starting set |
|---|---|
Features::builder() |
the nine defaults |
Features::default() |
the nine defaults |
Feature::defaults() |
the nine defaults |
FeaturesBuilder::new() |
the nine defaults |
FeaturesBuilder::none() |
empty |
FeaturesBuilder::default() |
empty — see below |
FeaturesBuilder::default() is empty, not the defaults¶
This is the one trap on the page. FeaturesBuilder derives Default, and a derived
Default on a struct wrapping a HashSet produces an empty set. So:
FeaturesBuilder::new().build().iter().count() // 9
FeaturesBuilder::default().build().iter().count() // 0
FeaturesBuilder::default() behaves like FeaturesBuilder::none(), not like
FeaturesBuilder::new(). Features::default() is unaffected — it delegates to
builder() and gives you the nine.
Nothing warns about this, and a tool that reaches for ..Default::default() out of
habit ends up shipping a CLI with every built-in command hidden. Use
FeaturesBuilder::new() when you mean the defaults and FeaturesBuilder::none()
when you mean empty; treat FeaturesBuilder::default() as a synonym for the latter.
Reading a Features set¶
pub fn is_enabled(&self, feature: Feature) -> bool;
pub fn iter(&self) -> impl Iterator<Item = Feature> + '_;
Features is backed by a HashSet, so iter() yields no defined order and the
order can differ between runs of the same binary. Sort before printing anything a
user or a test will compare.
Features derives Debug and Clone. It does not implement PartialEq, so two
sets cannot be compared with ==; compare via is_enabled per feature, or collect
and sort.
What runtime features are not¶
Cargo features and rtb_app::Feature are separate mechanisms that share a word.
| Cargo features | rtb_app::Feature |
|
|---|---|---|
| Decided at | compile time, on the rtb umbrella crate |
runtime, per invocation |
| Effect when off | the command's code is not compiled and never registers into BUILTIN_COMMANDS |
the command is compiled and registered, but filtered out before clap sees it |
rtb-app itself declares no Cargo features at all, so nothing in this crate is
conditionally compiled.
Feature is #[non_exhaustive], so a match over it needs a _ => arm — that is
what allows a new built-in command to arrive in a minor release. For the same reason
Feature::all() returns &'static [Feature] rather than [Feature; 11]: an array's
length is part of its type, so returning one would make every new variant a breaking
change.
Related¶
- Commands —
CommandSpec::featureis what ties a command to a gate - Tool metadata —
Feature::Updategates every update field