cli-command

Crates.iocli-command
lib.rscli-command
version0.1.0
created_at2025-09-15 07:49:02.916968+00
updated_at2025-09-15 07:49:02.916968+00
descriptionA lightweight and ergonomic command-line argument parser for Rust
homepage
repositoryhttps://gitlab.com/Efimster/cli-command
max_upload_size
id1839562
size81,057
Efimster (Efimster)

documentation

README

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 Documentation License: 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

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:

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:

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

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

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

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

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:

# 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:

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:

# 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 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
Commit count: 15

cargo fmt