# cli-command
A **lightweight**, ergonomic command-line argument parser for Rust applications that prioritizes simplicity and performance with convenient macros for reduced boilerplate.
[](https://crates.io/crates/cli-command)
[](https://docs.rs/cli-command)
[](https://opensource.org/licenses/MIT)
## Why cli-command?
**Perfect for developers who want CLI parsing without the complexity.** While `clap` is powerful, it's often overkill for simple applications. `cli-command` fills the gap with a clean, intuitive API that's both lightweight and feature-rich.
### ๐ **Key Advantages**
- **Minimal Dependencies** - Only 3 lightweight dependencies (proc-macro2, quote, syn)
- **Dual API Design** - Both method-based and macro-based interfaces for different preferences
- **Macro Ergonomics** - `cli_args!` macro eliminates boilerplate while maintaining type safety
- **Generic Type Support** - Works with any `FromStr` type out of the box
- **Smart Error Messages** - Helpful error messages that guide users to solutions
- **Memory Efficient** - Minimal memory footprint with optimized parsing
- **Flexible Usage** - Choose between explicit method calls or convenient macros
### ๐ฏ **Perfect For**
- **Simple CLI tools** that need basic argument parsing
- **Performance-critical applications** where binary size matters
- **Learning projects** where you want to understand the parsing logic
- **Rapid prototyping** with macro-based argument extraction
- **Quick prototypes** that need CLI support without complexity
- **Applications** that benefit from both explicit and macro-based APIs
## Features
- ๐ **Minimal dependencies** - Only 3 lightweight dependencies for macro support
- ๐ฏ **Dual API design** - Both method-based and macro-based interfaces
- ๐ง **Flexible parsing** - Supports both `-` and `--` argument prefixes
- ๐ **Type conversion** - Built-in support for common types
- โก **Error handling** - Comprehensive error types with helpful messages
- ๐งช **Well tested** - Extensive test coverage
- ๐ **Generic defaults** - `get_argument_or_default<T>()` works with any type
- ๐ **Multiple values** - Support for arguments with multiple parameters
- ๐จ **Macro ergonomics** - `cli_args!` macro for boilerplate-free argument extraction
- ๐ญ **Command matching** - `cli_match!` macro for clean command routing
## Comparison with Other CLI Parsers
| **Dependencies** | 3 | 20+ | 0 | 1 |
| **Binary Size** | Minimal | Large | Minimal | Small |
| **Compile Time** | Fast | Slow | Fast | Fast |
| **API Style** | Method + Macro | Derive + Builder | Iterator-based | Derive |
| **Type Conversion** | Built-in + Generic | Built-in | Manual | Built-in |
| **Error Messages** | Helpful | Excellent | Basic | Good |
| **Learning Curve** | Easy | Steep | Medium | Easy |
| **Macros Available** | Optional | Yes | No | Yes |
**When to choose cli-command:**
- You want minimal dependencies and fast compilation
- You prefer flexible APIs (both method-based and macro-based)
- You need basic to intermediate CLI parsing features
- Binary size and performance are important
- You want to understand the parsing logic
- You like the ergonomics of macros but want the option to use explicit APIs
## Basic Usage
```rust
use cli_command::{parse_command_line, Command};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Parse command line arguments
let cmd = parse_command_line()?;
// Get a simple argument
if let Some(port) = cmd.get_argument("port") {
println!("Port: {}", port);
}
// Get a required argument with type conversion
let threads: usize = cmd.get_argument_mandatory("threads")?;
println!("Threads: {}", threads);
// Get argument with default value (works with any FromStr type!)
let timeout: u64 = cmd.get_argument_or_default("timeout", 30)?;
let host: String = cmd.get_argument_or_default("host", "localhost".to_string())?;
let debug: bool = cmd.get_argument_or_default("debug", false)?;
println!("Timeout: {}, Host: {}, Debug: {}", timeout, host, debug);
Ok(())
}
```
### ๐ฏ **What Makes This Special**
Notice how `get_argument_or_default<T>()` works with **any type** that implements `FromStr` - no special handling needed! This generic approach makes the API both powerful and intuitive.
## Macro-Based Usage
For even more ergonomic code, `cli-command` provides convenient macros that eliminate boilerplate while maintaining the same performance:
### `cli_args!` Macro
The `cli_args!` macro allows you to extract multiple arguments with defaults in a single expression:
```rust
use cli_command::{cli_args, parse_command_line};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Extract all arguments with defaults in one line!
let (port, host, workers, verbose) = cli_args!(
port: u16 = 8080,
host: String = "localhost".to_string(),
workers: usize = 4,
verbose: bool = false
);
println!("Server: {}:{} (workers: {}, verbose: {})", host, port, workers, verbose);
Ok(())
}
```
### `cli_match!` Macro
The `cli_match!` macro provides clean command routing and automatically parses the command line:
```rust
use cli_command::{cli_match, cli_args};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// No need to manually parse command line - cli_match! does it automatically!
cli_match! {
"serve" => {
let (port, host) = cli_args!(
port: u16 = 8080,
host: String = "localhost".to_string()
);
start_server(port, host)
},
"build" => {
let (output, release) = cli_args!(
output: String = "dist".to_string(),
release: bool = false
);
build_project(output, release)
},
"help" => print_help(),
_ => {
eprintln!("Unknown command");
print_help();
Ok(())
}
}
}
```
### ๐ **Macro Benefits**
- **Reduced Boilerplate** - Extract multiple arguments in one line
- **Type Safety** - Full compile-time type checking
- **Same Performance** - Macros expand to the same method calls
- **Optional Usage** - Use macros when convenient, methods when explicit
- **Automatic Parsing** - No need to call `parse_command_line()` manually
- **Clean Command Routing** - `cli_match!` handles command parsing and routing automatically
## Examples
### Simple CLI Tool
```rust
use cli_command::{parse_command_line, Command};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cmd = parse_command_line()?;
match cmd.name.as_str() {
"serve" => {
let port: u16 = cmd.get_argument_or_default("port", 8080)?;
let host = cmd.get_argument_or_default("host", "localhost".to_string())?;
println!("Serving on {}:{}", host, port);
}
"build" => {
let output = cmd.get_argument_or_default("output", "dist".to_string())?;
let release = cmd.contains_argument("release");
println!("Building to {} (release: {})", output, release);
}
_ => {
println!("Unknown command: {}", cmd.name);
return Ok(());
}
}
Ok(())
}
```
### Server Configuration
```rust
use cli_command::{parse_command_line, Command};
use std::net::SocketAddr;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cmd = parse_command_line()?;
// Parse server configuration
let addr: SocketAddr = cmd.get_argument_mandatory("bind")?;
let workers: usize = cmd.get_argument_or_default("workers", 4)?;
let max_connections: usize = cmd.get_argument_or_default("max-connections", 1000)?;
let enable_ssl = cmd.contains_argument("ssl");
println!("Server configuration:");
println!(" Address: {}", addr);
println!(" Workers: {}", workers);
println!(" Max connections: {}", max_connections);
println!(" SSL enabled: {}", enable_ssl);
Ok(())
}
```
## API Reference
### Command Structure
```rust
pub struct Command {
pub name: String, // The command name (first non-flag argument)
pub arguments: HashMap<String, Box<[String]>>, // Parsed arguments
}
```
### Argument Access Methods
#### Optional Arguments
- `get_argument(name)` - Get first value of an argument
- `get_argument_nth_parameter(name, nth)` - Get nth value of an argument
- `get_argument_all(name)` - Get all values of an argument
- `contains_argument(name)` - Check if argument exists
#### Required Arguments
- `get_argument_mandatory(name)` - Get first value, error if missing
- `get_argument_nth_param_mandatory(name, nth)` - Get nth value, error if missing
- `get_argument_mandatory_all(name)` - Get all values, error if missing
#### Type Conversion
- `get_argument_usize(name)` - Convert to `usize`
- `get_argument_mandatory_usize(name)` - Convert to `usize`, error if missing
- `get_argument_or_default(name, default)` - Get value or return default
### Error Handling
```rust
use cli_command::{CliError, CliErrorKind};
match cmd.get_argument_mandatory("required_arg") {
Ok(value) => println!("Got: {}", value),
Err(CliError { kind: CliErrorKind::MissingArgument(arg), .. }) => {
eprintln!("Missing required argument: {}", arg);
}
Err(e) => eprintln!("Error: {}", e),
}
```
## Command Line Format
The parser supports standard Unix-style command line arguments:
```bash
# Command with arguments
myapp serve --port 8080 --host localhost --workers 4
# Multiple values for same argument
myapp build --input file1.rs file2.rs --output dist/
# Short and long forms
myapp -v --verbose --quiet
# Boolean flags (no value needed)
myapp --enable-feature --no-cache
```
## Error Handling
`cli-command` provides **helpful error messages** that guide users to solutions:
```rust
use cli_command::{CliError, CliErrorKind};
match cmd.get_argument_mandatory("required_arg") {
Ok(value) => println!("Got: {}", value),
Err(CliError { kind: CliErrorKind::MissingArgument(arg), .. }) => {
eprintln!("Missing required argument: {}", arg);
}
Err(e) => eprintln!("Error: {}", e),
}
```
### Error Types
- `MissingArgument` - Required argument not provided (with helpful usage hint)
- `MissingParameter` - Required parameter at specific position not provided
- `ParseCommandLine` - General command line parsing error
- `Inner` - Wrapped error from type conversion
## Performance
- **Minimal dependencies** = faster compilation and smaller binaries than clap
- **Minimal memory allocation** during parsing
- **Fast type conversion** with built-in optimizations
- **Optional macros** = use them when convenient, avoid when not needed
- **Zero runtime overhead** = macros expand to the same method calls
## Command Line Format
The parser supports standard Unix-style command line arguments:
```bash
# Command with arguments
myapp serve --port 8080 --host localhost --workers 4
# Multiple values for same argument
myapp build --input file1.rs file2.rs --output dist/
# Short and long forms
myapp -v --verbose --quiet
# Boolean flags (no value needed)
myapp --enable-feature --no-cache
```
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## Changelog
### 0.0.5 (Upcoming)
- Added `cli_args!` macro for ergonomic argument extraction
- Added `cli_match!` macro for clean command routing
- Introduced minimal dependencies (proc-macro2, quote, syn) for macro support
- Maintained backward compatibility with method-based API
- Enhanced usability with dual API design
### 0.0.4
- Initial release
- Basic command line parsing
- Type conversion support
- Error handling