FtrIO (Rust)
Attribute-based feature toggles for Rust — a faithful port of the .NET FtrIO library, with the Python port as a second reference.
The flags are a file you own, sitting right next to your code. No dashboard, no SaaS, no network round-trip on the hot path: radical ownership of your feature toggles.
The smallest example
Decorate a function with #[toggle]. It runs only when its toggle is on; otherwise it returns
Default::default().
use toggle;
// key derives from the fn name: "send_welcome_email"
// appsettings.json
With no appsettings.json on disk at all, every toggle defaults to on — the offline-safe
default, so a service always runs.
Installation
Optional providers are behind features, mirroring the separate .NET provider projects:
The attribute
#[toggle] and #[toggle_async] are procedural attribute macros — compile-time code
transformation, the closest analogue of the .NET AspectInjector attribute of any target language.
use ;
// explicit key overrides the derived one
// gating runs synchronously at call time; the result is awaitable either way
async
Because the off-path returns Default::default(), a gated function's return type must implement
Default ((), Option<T>, integers, String, etc.). A misconfiguration (missing key,
unparseable value) panics out of the decorated function — the same way the .NET woven aspect throws.
The ftrio lint step is there to catch that at build time.
The builder pipeline
Assemble a parser with the strategies you want, then install it as the ambient instance:
use Arc;
use ;
let parser = new
.with_base_path
.with_percentage_rollout
.with_blue_green
.with_context_strategies // user targeting, attribute rules, A/B testing
.with_overrides // requires a context accessor
.with_context_accessor
.build
.expect;
configure;
The value grammar (all case-insensitive):
| Grammar | Example | Strategy |
|---|---|---|
| boolean | true, false, 1, 0 |
BooleanStrategy |
| percentage | 50% |
PercentageRolloutStrategy |
| slot | blue, green |
BlueGreenStrategy |
| user list | users:alice,bob |
UserTargetingStrategy |
| attribute rule | attribute:plan equals premium |
AttributeRuleStrategy |
| A/B | ab:50, ab:50:round2 |
AbTestStrategy |
Resolution order: no config file → true; then a per-user TogglesOverrides entry wins
unconditionally; otherwise the first strategy whose grammar matches decides, with BooleanStrategy
always last.
Providers and the buffer model
AppSettingsToggleParser— theappsettings.jsonfile reader.EnvironmentVariableToggleParser— readsFTRIO__Toggles__<Key>.CompositeToggleParser— tries several sources in order, first-wins.ToggleProviderBuffer— stages toggle writes (Mutex<HashMap>, last-write-wins), flushes on a background interval thread with an atomic temp-file-plus-rename, and performs a final flush onDrop.
The ftrio CLI
ftrio lint is the port of the Roslyn analyzer ToggleConfigAnalyzer: it walks .rs files with
syn, resolves each #[toggle] key, and fails the build if the key is missing from Toggles.
Configuration
appsettings.json sections: FtrIO (settings), Toggles (key → value), TogglesOverrides
(toggle_key → { user_id → bool }).
FtrIO keys: ReloadOnChange (bool), FlushInterval (int seconds, default 5), Environment
(string), BlueGreen:CurrentSlot, BlueGreen:KnownSlots (comma-separated). Environment resolution:
FtrIO:Environment, then ASPNETCORE_ENVIRONMENT, then DOTNET_ENVIRONMENT, then (additive)
FTRIO_ENVIRONMENT.
Playground
ftrio-playground is a standalone, educational crate (not part of the published workspace, never
published). It consumes ftrio the way a real user would, so run it from its own directory:
It ships its own appsettings.json next to the code (with ReloadOnChange on, so you can edit it
live) and prints, for each gated function, the key, its raw value, the resolved decision, and the
context used — then runs (or skips) the decorated body.
Development
These gates are the Rust analogue of the style-guide conformance the Python port enforced with
ruff. Acronyms are one word (FtrIo, AbTestStrategy, HttpToggleParser).
Releasing and changelog
The export-manifest → release-check pair is a cross-tool contract: export a manifest of the
toggles your code uses, then gate a release on every one of them existing in the target environment's
config (exit 0 ready, 1 blocked, 2 manifest error, 3 config error).
See PORTING_NOTES.md for the full record of how each .NET mechanism maps to
Rust.