Rust CLI Development

Building powerful command-line applications with Rust

⚡ Rust CLI Applications

Rust excels at building fast, reliable command-line tools. With excellent argument parsing, error handling, and cross-platform support, Rust makes CLI development efficient and robust.


// Simple CLI application
use std::env;

fn main() {
    let args: Vec = env::args().collect();
    println!("Hello, {}!", args.get(1).unwrap_or(&"World".to_string()));
}
                                    

Usage:

./my_cli Alice → "Hello, Alice!"

Essential CLI Crates

🔧

Clap

Command line argument parser

use clap::Parser;

#[derive(Parser)]
struct Args {
    name: String,
}
🎨

Colored

Add colors to terminal output

use colored::*;

println!("{}", "Success!".green());
println!("{}", "Error!".red());
📊

Indicatif

Progress bars and spinners

let bar = ProgressBar::new(100);
for i in 0..100 {
    bar.inc(1);
}
📝

Dialoguer

Interactive prompts and menus

let name: String = Input::new()
    .with_prompt("Your name")
    .interact()?;

🔹 Basic CLI with Clap

Create a professional CLI application:

# Cargo.toml
[dependencies]
clap = { version = "4.0", features = ["derive"] }
colored = "2.0"
// main.rs
use clap::{Parser, Subcommand};
use colored::*;

#[derive(Parser)]
#[command(name = "mytool")]
#[command(about = "A simple CLI tool", long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Option,
}

#[derive(Subcommand)]
enum Commands {
    /// Greet someone
    Greet {
        /// Name of the person to greet
        #[arg(short, long)]
        name: String,
        /// Number of times to greet
        #[arg(short, long, default_value_t = 1)]
        count: u8,
    },
    /// Show version information
    Version,
}

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

    match &cli.command {
        Some(Commands::Greet { name, count }) => {
            for _ in 0..*count {
                println!("{} {}!", "Hello".green().bold(), name.cyan());
            }
        }
        Some(Commands::Version) => {
            println!("{} {}", "mytool".bold(), "v1.0.0".dimmed());
        }
        None => {
            println!("{}", "Use --help for usage information".yellow());
        }
    }
}

Usage Examples:

  • mytool greet --name Alice
  • mytool greet -n Bob -c 3
  • mytool version

🔹 File Processing CLI

Build a tool that processes files:

use clap::Parser;
use std::fs;
use std::path::PathBuf;
use colored::*;

#[derive(Parser)]
struct Args {
    /// Input file to process
    #[arg(short, long)]
    input: PathBuf,
    
    /// Output file (optional)
    #[arg(short, long)]
    output: Option,
    
    /// Convert to uppercase
    #[arg(long)]
    uppercase: bool,
    
    /// Count lines
    #[arg(long)]
    count_lines: bool,
}

fn main() -> Result<(), Box> {
    let args = Args::parse();
    
    // Read input file
    let content = fs::read_to_string(&args.input)?;
    println!("{} Reading file: {}", "✓".green(), args.input.display());
    
    let mut result = content;
    
    // Process content
    if args.uppercase {
        result = result.to_uppercase();
        println!("{} Converted to uppercase", "✓".green());
    }
    
    if args.count_lines {
        let line_count = result.lines().count();
        println!("{} Line count: {}", "ℹ".blue(), line_count.to_string().bold());
    }
    
    // Write output
    match args.output {
        Some(output_path) => {
            fs::write(&output_path, result)?;
            println!("{} Written to: {}", "✓".green(), output_path.display());
        }
        None => {
            println!("\n{}", "Output:".bold());
            println!("{}", result);
        }
    }
    
    Ok(())
}

Usage:

mytool -i input.txt -o output.txt --uppercase --count-lines

🔹 Interactive CLI with Progress

Create an interactive CLI with progress indicators:

# Add to Cargo.toml
indicatif = "0.17"
dialoguer = "0.10"
tokio = { version = "1.0", features = ["full"] }
use dialoguer::{Input, Select, Confirm};
use indicatif::{ProgressBar, ProgressStyle};
use std::time::Duration;
use tokio::time::sleep;

#[tokio::main]
async fn main() -> Result<(), Box> {
    println!("{}", "🚀 Welcome to Interactive CLI".bold().cyan());
    
    // Get user input
    let name: String = Input::new()
        .with_prompt("What's your name?")
        .interact()?;
    
    // Show selection menu
    let selections = &["Process files", "Download data", "Generate report"];
    let selection = Select::new()
        .with_prompt("What would you like to do?")
        .default(0)
        .items(&selections[..])
        .interact()?;
    
    // Confirm action
    let confirmed = Confirm::new()
        .with_prompt(format!("Hello {}! Proceed with '{}'?", name, selections[selection]))
        .interact()?;
    
    if confirmed {
        // Show progress bar
        let pb = ProgressBar::new(100);
        pb.set_style(ProgressStyle::default_bar()
            .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos:>7}/{len:7} {msg}")
            .unwrap()
            .progress_chars("##-"));
        
        for i in 0..100 {
            pb.set_message(format!("Processing item {}", i + 1));
            pb.inc(1);
            sleep(Duration::from_millis(50)).await;
        }
        
        pb.finish_with_message("✅ Task completed successfully!");
        println!("{} Thanks for using our CLI, {}!", "🎉".bold(), name.green());
    } else {
        println!("{}", "Operation cancelled.".yellow());
    }
    
    Ok(())
}

Features:

  • Interactive prompts
  • Selection menus
  • Progress bars
  • Colored output

🧠 Test Your Knowledge

Which crate is most popular for parsing command-line arguments in Rust?