# cmdkit-macros
Procedural macros for generating `cmdkit` command strategies from plain Rust functions.
## What It Does
This crate provides one attribute macro:
- `#[strategy]`
It transforms a free function into a concrete type that implements `cmdkit::CommandStrategy`.
Given a function like `create_directory`, the macro generates:
- A strategy type named `CreateDirectory`
- A constructor `CreateDirectory::new()`
- A helper factory function `create_directory_strategy()`
- An `impl cmdkit::CommandStrategy` with `execute(...)`
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
cmdkit = "0.1"
cmdkit-macros = "0.1"
```
## Quick Example
```rust
use cmdkit::{Argument, CliCore, Command, StrategyError, Switch};
use cmdkit_macros::strategy;
#[strategy]
fn create_directory(
_options: Vec<Switch>,
arguments: Vec<Argument>,
_subcommands: Vec<String>,
) -> Result<(), StrategyError> {
let path = arguments
.iter()
.find(|argument| argument.name == "path")
.and_then(|argument| argument.value.as_deref())
.ok_or_else(|| StrategyError::invalid_arguments("missing path"))?;
std::fs::create_dir(std::path::Path::new(path))
.map_err(|e| StrategyError::execution(format!("Failed to create directory: {e}")))
}
fn main() {
let core = CliCore::new();
core.register(Command::new(
"create",
"Create a directory",
CreateDirectory::new(),
));
// You can also use create_directory_strategy()
// generated by the macro.
}
```
## Required Function Signature
A function annotated with `#[strategy]` must:
1. Be a free function (not a method).
2. Not be `async`.
3. Accept exactly these three parameters in this order:
- `options: Vec<Switch>`
- `arguments: Vec<Argument>`
- `subcommands: Vec<String>`
4. Return `Result<(), cmdkit::StrategyError>`.
Example accepted shape:
```rust
#[strategy]
fn my_command(
options: Vec<cmdkit::Switch>,
arguments: Vec<cmdkit::Argument>,
subcommands: Vec<String>,
) -> Result<(), cmdkit::StrategyError> {
let _ = (options, arguments, subcommands);
Ok(())
}
```
## Naming Rules
The generated type name is UpperCamelCase from the function name.
- `simple_cli_strategy` -> `SimpleCliStrategy`
- `create_directory` -> `CreateDirectory`
## Macro Expansion Summary
For:
```rust
#[strategy]
fn sample_name(
options: Vec<cmdkit::Switch>,
arguments: Vec<cmdkit::Argument>,
subcommands: Vec<String>,
) -> Result<(), cmdkit::StrategyError> {
Ok(())
}
```
The macro generates equivalents of:
```rust
pub struct SampleName;
impl SampleName {
pub fn new() -> Self {
Self
}
}
impl cmdkit::CommandStrategy for SampleName {
fn execute(
&self,
options: Vec<cmdkit::Switch>,
arguments: Vec<cmdkit::Argument>,
subcommands: Vec<String>,
) -> Result<(), cmdkit::StrategyError> {
let _ = (options, arguments, subcommands);
Ok(())
}
}
pub fn sample_name_strategy() -> SampleName {
SampleName::new()
}
```
## Development
Run tests:
```bash
cargo test
```
## License
GPL-3.0-or-later