command!() { /* proc-macro */ }Expand description
A procedural macro for generating typed Command implementations with compile-time validation.
This macro provides pure value-based argument passing with compile-time type safety by generating:
- Typed Args structs with proper field types passed by value only
- Commands that accept Args structs directly (no
HashMapusage) - Individual typed variables available directly in command scope
- Compile-time validation of argument types and required/optional fields
- Zero runtime argument parsing or validation overhead
§Syntax
ⓘ
command! {
CommandName {
#[required]
arg_name: Type,
#[optional]
opt_name: Type,
} => {
// Command implementation body
// Individual typed variables are available in scope
}
}§Generated Code
The macro generates:
- A
CommandNameArgsstruct with typed fields (required fields asType, optional asOption<Type>) - A
CommandNamestruct implementingCommandtrait with typedrun(args: CommandNameArgs)method - Individual typed variables extracted from the Args struct and available in the command body
- Pure compile-time type validation with no runtime overhead
§Examples
§Simple command with no arguments
command! {
GetVersion {
} => {
Ok(Some(CmdResult::String("1.0.0".to_string())))
}
}
let result = GetVersion::new().run().await.unwrap();§Command with required arguments (server-compatible types)
// This creates a commodity in the financial system
command! {
CreateCommodity {
#[required]
symbol: String,
#[required]
name: String,
#[required]
user_id: Uuid,
} => {
// Individual typed variables are automatically available
Ok(Some(CmdResult::String(format!(
"Created commodity {} ({}) for user {}",
name, symbol, user_id
))))
}
}
let result = CreateCommodity::new()
.symbol("USD".to_string())
.name("US Dollar".to_string())
.user_id(uuid::Uuid::new_v4())
.run()
.await
.unwrap();§Command with optional arguments
command! {
ListTransactions {
#[required]
user_id: Uuid,
#[optional]
account: String,
} => {
let filter = if let Some(account) = account {
format!(" for account {}", account)
} else {
String::new()
};
Ok(Some(CmdResult::String(format!("Listing transactions for user {}{}", user_id, filter))))
}
}§Command with mixed required and optional arguments
command! {
CreateUserCommand {
#[required]
user_id: i64,
#[required]
username: String,
#[optional]
email: String,
#[optional]
is_admin: bool,
} => {
let email_str = email.map_or_else(|| format!("{}@example.com", username), |s| s.to_string());
let admin_status = is_admin.unwrap_or(false);
let message = format!(
"Created user {} (ID: {}, Email: {}, Admin: {})",
username, user_id, email_str, admin_status
);
Ok(Some(CmdResult::Success(message)))
}
}
let result = CreateUserCommand::new()
.user_id(123)
.username("alice".to_string())
.is_admin(true)
.run()
.await
.unwrap();§Server-compatible Command implementation
command! {
CalculateCommand {
#[required]
a: i64,
#[required]
b: i64,
} => {
let result = a + b;
Ok(Some(CmdResult::Success(format!("{} + {} = {}", a, b, result))))
}
}
let result = CalculateCommand::new()
.a(10)
.b(20)
.run()
.await
.unwrap();§Migration from Manual Commands
The macro makes it easy to migrate from manual Command implementations:
ⓘ
// BEFORE: Manual implementation
#[derive(Debug)]
pub struct GetConfig;
#[async_trait]
impl Command for GetConfig {
async fn run<'a>(&self, args: &'a HashMap<&'a str, &'a Argument>) -> Result<Option<CmdResult>, CmdError> {
if let Some(Argument::String(name)) = args.get("name") {
Ok(config(name).await?.map(|v| CmdResult::String(v)))
} else {
Err(CmdError::Args("No field name provided".to_string()))
}
}
}
// AFTER: Using the macro
command! {
GetConfig {
#[required]
name: String,
} => {
Ok(config(name).await?.map(|v| CmdResult::String(v)))
}
}§Error Handling
The new pure typed system provides compile-time error prevention:
- Missing required arguments are compile-time errors (cannot compile without them)
- Invalid argument types are compile-time errors (type checking at build time)
- Runtime errors only occur in the command body logic itself
- No argument validation overhead at runtime
§Supported Argument Types
The macro supports any Rust type for arguments:
String- Text argumentsi64,u64, etc. - Integer argumentsbool- Boolean argumentsRational64- Rational number arguments (for financial precision)Uuid- UUID argumentsVec<u8>- Binary data argumentsDateTime<Utc>-DateTimearguments- Custom types - Any type can be used as an argument
Option<T>- Automatically applied for optional arguments