cli-command 0.1.0

A lightweight and ergonomic command-line argument parser for Rust
Documentation
# cli-command

A **lightweight**, ergonomic command-line argument parser for Rust applications that prioritizes simplicity and performance with convenient macros for reduced boilerplate.

[![Crates.io](https://img.shields.io/crates/v/cli-command.svg)](https://crates.io/crates/cli-command)
[![Documentation](https://docs.rs/cli-command/badge.svg)](https://docs.rs/cli-command)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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

| Feature | cli-command | clap | pico-args | gumdrop |
|---------|-------------|------|-----------|---------|
| **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