argh

A derive attribute-based argument parser developed by the Google Fuchsia team. Simple and easy-to-use design.

rustcliderivegooglefuchsia

Framework

argh

Overview

argh is a derive attribute-based argument parser developed by the Google Fuchsia team. It's designed with emphasis on code size optimization and compliance with Google Fuchsia command-line tool specifications. It features simple and easy-to-use design, has adoption record in Google projects, and is supported by developers who prefer simple APIs.

Details

argh is a command-line argument parser born during the development of Google's Fuchsia operating system. It's designed with emphasis on minimizing code size and high performance, enabling intuitive and declarative argument definitions through the use of derive macros. It has a track record of use within Google and is particularly adopted in projects with size constraints or those that prioritize simplicity.

Key Features

  • Derive Macros: Automatic parser generation with #[derive(FromArgs)]
  • Code Size Optimization: Generated code size kept to a minimum
  • Simple API: Intuitive and easy-to-understand attribute-based design
  • Type Safety: Strong typing leveraging Rust's type system
  • Subcommands: Subcommand support using enums
  • Automatic Help: Automatic help generation from documentation comments
  • Fuchsia Compliant: Compliant with Google Fuchsia command-line specifications

Pros and Cons

Pros

  • Lightweight: Very small code footprint
  • Simple: Low learning cost, can start using immediately
  • Fast: High performance through optimized parser
  • Google Quality: Track record and quality assurance within Google
  • Declarative: CLI specifications clearly expressed through struct definitions

Cons

  • Limited Features: Advanced CLI features are more limited than other libraries
  • Ecosystem: Limited adoption in the Rust ecosystem
  • Customization: Fewer fine-grained customization options
  • Community: Smaller community compared to clap or structopt

Key Links

Example Usage

use argh::FromArgs;

/// File processing tool
#[derive(FromArgs, PartialEq, Debug)]
struct AppArgs {
    /// Enable verbose output
    #[argh(switch, short = 'v')]
    verbose: bool,

    /// Specify output format
    #[argh(option, short = 'f')]
    format: Option<String>,

    /// Specify number of processes
    #[argh(option, default = "1")]
    count: usize,

    /// Input file path
    #[argh(positional)]
    input_file: String,

    /// Output file path (optional)
    #[argh(positional)]
    output_file: Option<String>,

    #[argh(subcommand)]
    command: Option<Commands>,
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum Commands {
    /// Convert files
    Convert(ConvertArgs),
    /// Validate files
    Validate(ValidateArgs),
    /// Display statistics
    Stats(StatsArgs),
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "convert")]
/// Convert file format
struct ConvertArgs {
    /// Target format
    #[argh(option, short = 't')]
    target_format: String,

    /// Quality setting (1-100)
    #[argh(option, default = "80")]
    quality: u8,

    /// Allow overwrite
    #[argh(switch)]
    overwrite: bool,
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "validate")]
/// Validate file format
struct ValidateArgs {
    /// Execute strict validation
    #[argh(switch)]
    strict: bool,

    /// Output detailed report
    #[argh(switch)]
    detailed: bool,
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "stats")]
/// Display file statistics
struct StatsArgs {
    /// Display in human-readable format
    #[argh(switch, short = 'h')]
    human_readable: bool,

    /// Output in JSON format
    #[argh(switch)]
    json: bool,
}

fn main() {
    let args: AppArgs = argh::from_env();

    if args.verbose {
        println!("Verbose mode enabled");
        println!("Arguments: {:#?}", args);
    }

    println!("Input file: {}", args.input_file);
    
    if let Some(output) = &args.output_file {
        println!("Output file: {}", output);
    }

    if let Some(format) = &args.format {
        println!("Output format: {}", format);
    }

    println!("Process count: {}", args.count);

    // Handle subcommands
    match args.command {
        Some(Commands::Convert(convert_args)) => {
            println!("Executing conversion:");
            println!("  Target format: {}", convert_args.target_format);
            println!("  Quality: {}", convert_args.quality);
            if convert_args.overwrite {
                println!("  Overwrite mode enabled");
            }
        }

        Some(Commands::Validate(validate_args)) => {
            println!("Executing validation:");
            if validate_args.strict {
                println!("  Executing strict validation");
            }
            if validate_args.detailed {
                println!("  Generating detailed report");
            }
        }

        Some(Commands::Stats(stats_args)) => {
            println!("Displaying statistics:");
            if stats_args.human_readable {
                println!("  Human-readable format");
            }
            if stats_args.json {
                println!("  JSON format output");
            }
        }

        None => {
            println!("Executing basic file processing");
        }
    }
}

// More complex example: nested subcommands
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum DatabaseCommands {
    /// Database management commands
    Db(DbArgs),
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "db")]
/// Database operations
struct DbArgs {
    #[argh(subcommand)]
    operation: DbOperations,
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum DbOperations {
    /// Connect to database
    Connect(ConnectArgs),
    /// Execute query
    Query(QueryArgs),
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "connect")]
/// Database connection
struct ConnectArgs {
    /// Database URL
    #[argh(positional)]
    url: String,

    /// Timeout in seconds
    #[argh(option, default = "30")]
    timeout: u32,
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "query")]
/// Execute SQL query
struct QueryArgs {
    /// SQL statement to execute
    #[argh(positional)]
    sql: String,

    /// Maximum number of result rows
    #[argh(option, default = "100")]
    limit: usize,
}