#Command-Line Tools

Rust excels at building fast, reliable command-line tools. The clap crate handles argument parsing while the ecosystem provides crates for colors, progress bars, and interactive prompts.

#Argument Parsing with Clap

Define your CLI interface with clap's derive API:

use clap::Parser;

#[derive(Parser)]
#[command(name = "greet", about = "A friendly greeting tool")]
struct Args {
    /// Name of the person to greet
    name: String,

    /// Number of times to greet
    #[arg(short, long, default_value_t = 1)]
    count: u32,

    /// Use uppercase greeting
    #[arg(short, long)]
    uppercase: bool,
}

fn main() {
    let args = Args::parse();

    for _ in 0..args.count {
        let greeting = format!("Hello, {}!", args.name);
        if args.uppercase {
            println!("{}", greeting.to_uppercase());
        } else {
            println!("{greeting}");
        }
    }
}

#Subcommands

Structure complex CLIs with subcommands:

use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(name = "tasks", about = "Task management CLI")]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Add a new task
    Add {
        /// Task description
        description: String,

        /// Priority level (1-5)
        #[arg(short, long, default_value_t = 3)]
        priority: u8,
    },

    /// List all tasks
    List {
        /// Show only completed tasks
        #[arg(long)]
        done: bool,
    },

    /// Mark a task as complete
    Done {
        /// Task ID
        id: u32,
    },
}

fn main() {
    let cli = Cli::parse();

    match cli.command {
        Commands::Add { description, priority } => {
            println!("Adding task (priority {priority}): {description}");
        }
        Commands::List { done } => {
            if done {
                println!("Showing completed tasks");
            } else {
                println!("Showing all tasks");
            }
        }
        Commands::Done { id } => {
            println!("Marking task {id} as complete");
        }
    }
}

#Colored Output

Use the colored crate for terminal colors:

use colored::Colorize;

fn print_status(passed: usize, failed: usize) {
    println!("{}", "Test Results".bold().underline());
    println!("  {} {}", "PASSED".green().bold(), passed);
    if failed > 0 {
        println!("  {} {}", "FAILED".red().bold(), failed);
    }
}

fn print_warning(message: &str) {
    eprintln!("{} {message}", "warning:".yellow().bold());
}

#Progress Bars

Show progress for long-running operations with indicatif:

use indicatif::{ProgressBar, ProgressStyle};
use std::time::Duration;

fn process_files(files: &[String]) {
    let progress = ProgressBar::new(files.len() as u64);
    progress.set_style(
        ProgressStyle::default_bar()
            .template("{spinner:.green} [{bar:40.cyan/blue}] {pos}/{len} {msg}")
            .unwrap()
            .progress_chars("#>-"),
    );

    for file in files {
        progress.set_message(file.clone());
        process_single_file(file);
        progress.inc(1);
    }

    progress.finish_with_message("Done!");
}

#Reading stdin

Process piped input:

use std::io::{self, BufRead};

fn count_lines() -> io::Result<()> {
    let stdin = io::stdin();
    let mut line_count = 0;
    let mut word_count = 0;

    for line in stdin.lock().lines() {
        let line = line?;
        line_count += 1;
        word_count += line.split_whitespace().count();
    }

    println!("{line_count} lines, {word_count} words");
    Ok(())
}

#Exit Codes

Communicate success or failure to the shell:

use std::process::ExitCode;

fn main() -> ExitCode {
    match run() {
        Ok(()) => ExitCode::SUCCESS,
        Err(error) => {
            eprintln!("error: {error}");
            ExitCode::FAILURE
        }
    }
}

fn run() -> Result<(), Box<dyn std::error::Error>> {
    let config = load_config()?;
    process(&config)?;
    Ok(())
}

#Summary

Use clap for argument parsing — its derive API is clean and generates help text automatically. Add colored for terminal output, indicatif for progress bars, and dialoguer for interactive prompts. Rust's small binary sizes and fast startup make it an ideal choice for CLI tools.