Procedural Macros
toggly-macros adds declarative gates around the same client's local evaluations. Macros do not initialize a client, authenticate a user, fetch a separate context or assign variants.
Installation
[dependencies]
toggly = "0.6"
toggly-macros = "0.6"
Use these functions from an async application with an already initialized client, such as the quick start.
#[feature_flag] Attribute
With Custom Client
With Custom Context
With Fallback
Specify the feature key, client expression, context expression and compatible fallback explicitly:
use toggly::{EvalContext, TogglyClient};
use toggly_macros::feature_flag;
fn classic_dashboard() -> &'static str { "Classic dashboard" }
#[feature_flag(
feature = "new-dashboard",
client = "client",
context = "context",
fallback = "classic_dashboard()"
)]
pub async fn dashboard(client: &TogglyClient, context: EvalContext) -> &'static str {
"New dashboard"
}
When the flag is false, the macro returns the fallback before executing the function body. Evaluation errors are converted to false. Without an explicit client expression, the macro expects a variable named toggly_client; without context, it uses EvalContext::default(). Explicit parameters make dependencies and ownership clear.
Negated Check
use toggly::{EvalContext, TogglyClient};
use toggly_macros::feature_flag;
#[feature_flag(
feature = "new-dashboard", client = "client", context = "context",
negate = true, fallback = "false"
)]
pub async fn show_classic(client: &TogglyClient, context: EvalContext) -> bool {
true
}
Full Example
Call dashboard(&client, context).await from your async handler, then render the returned text. Keep client initialization and shutdown at application scope. For errors that must be propagated rather than converted to a fallback, call client.is_enabled(...).await? directly instead of using this attribute.
#[derive(FeatureFlags)] Derive Macro
Generated Methods
Usage
use toggly::{EvalContext, TogglyClient};
use toggly_macros::FeatureFlags;
#[derive(FeatureFlags)]
pub enum Features {
#[toggly(key = "new-dashboard")]
NewDashboard,
#[toggly(key = "api-v2")]
ApiV2,
}
pub async fn check_features(client: &TogglyClient, context: EvalContext)
-> toggly::Result<bool>
{
println!("Checking {}", Features::NewDashboard.key());
Features::NewDashboard.is_enabled(client, context).await
}
The derive generates key(), default_value(), is_enabled(...) and is_disabled(...). Use #[toggly(...)], not #[feature(...)].
With Default Values
use toggly_macros::FeatureFlags;
#[derive(FeatureFlags)]
pub enum Metadata {
#[toggly(key = "optional-banner", default = true)]
OptionalBanner,
}
Metadata::OptionalBanner.default_value() returns metadata for application code. It does not override the client's evaluation of an undefined flag: generated is_enabled delegates to the client. Do not rely on the derive's default as a missing-flag fallback.
feature_gate! Macro
The expression macro selects a block. Both branches must have compatible Rust result types:
use toggly::{EvalContext, TogglyClient};
use toggly_macros::feature_gate;
pub async fn label(client: &TogglyClient, context: EvalContext) -> &'static str {
feature_gate!(client, "new-dashboard", context, {
"New dashboard"
}, {
"Classic dashboard"
})
}
Without Fallback
Omit the second block only for unit-returning work. A false result executes no enabled block. Like the attribute macro, evaluation errors become false.
In Match Arms
A macro expression can be placed wherever its resulting Rust type is valid. It still awaits evaluation, so the enclosing function must be async.
Best Practices
Keep feature keys in an enum when useful, pass context explicitly, and give disabled paths meaningful product behavior. Multi-key gates remain a client API; feature_gate! accepts one literal key.
Limitations
These macros require an async context and an available client. They do not supply a public custom-filter interface or variant assignment. Derive defaults are metadata; macros do not replace authentication, request mapping or client lifecycle.