Rust Admin

O que Todo Dev Deve Saber sobre Argumentos de Linha de Comando com clap em Rust Já leu

Introdução ao Clap: Por Que Usar? O é a crate mais popular do Rust para parsing de argumentos de linha de comando. Diferentemente de fazer parsing manual com , o clap oferece uma API elegante, validação automática, geração de help text e tratamento de erros robusto. Se você está desenvolvendo ferramentas CLI em Rust, o clap é praticamente obrigatório — economiza horas de desenvolvimento e reduz drasticamente bugs relacionados a entrada do usuário. O clap oferece duas abordagens principais: a API de builder e a API de atributos derive. Vou focar na abordagem derive (mais moderna e recomendada), que usa macros para gerar o parsing automaticamente a partir de structs Rust. Instalação e Configuração Básica Setup Inicial Comece adicionando o clap ao seu : A feature ativa o macro , essencial para a abordagem que usaremos. Agora, um exemplo funcional bem simples: Execute com e veja a mágica acontecer. O clap automaticamente valida se o arquivo foi fornecido, gera mensagens

Introdução ao Clap: Por Que Usar?

O clap é a crate mais popular do Rust para parsing de argumentos de linha de comando. Diferentemente de fazer parsing manual com std::env::args(), o clap oferece uma API elegante, validação automática, geração de help text e tratamento de erros robusto. Se você está desenvolvendo ferramentas CLI em Rust, o clap é praticamente obrigatório — economiza horas de desenvolvimento e reduz drasticamente bugs relacionados a entrada do usuário.

O clap oferece duas abordagens principais: a API de builder e a API de atributos derive. Vou focar na abordagem derive (mais moderna e recomendada), que usa macros para gerar o parsing automaticamente a partir de structs Rust.

Instalação e Configuração Básica

Setup Inicial

Comece adicionando o clap ao seu Cargo.toml:

[dependencies]
clap = { version = "4.4", features = ["derive"] }

A feature derive ativa o macro #[derive(Parser)], essencial para a abordagem que usaremos. Agora, um exemplo funcional bem simples:

use clap::Parser;

#[derive(Parser, Debug)]
#[command(name = "MeuApp")]
#[command(about = "Uma ferramenta CLI demonstrativa", long_about = None)]
struct Args {
    /// Nome do arquivo a processar
    #[arg(short, long)]
    arquivo: String,

    /// Modo verboso
    #[arg(short, long)]
    verbose: bool,
}

fn main() {
    let args = Args::parse();
    println!("Arquivo: {}", args.arquivo);
    println!("Verbose: {}", args.verbose);
}

Execute com cargo run -- --arquivo dados.txt --verbose e veja a mágica acontecer. O clap automaticamente valida se o arquivo foi fornecido, gera mensagens de erro amigáveis e oferece --help sem você escrever uma linha de código para isso.

Argumentos, Opções e Subcomandos

Argumentos vs Opções

Argumentos posicionais (sem -- ou -) e opções (com -- ou -) funcionam diferentemente no clap:

use clap::Parser;

#[derive(Parser, Debug)]
struct Args {
    /// Argumento posicional obrigatório
    #[arg(value_name = "ORIGEM")]
    origem: String,

    /// Argumento posicional opcional
    #[arg(value_name = "DESTINO")]
    destino: Option<String>,

    /// Opção com valor
    #[arg(short = 'o', long = "output")]
    output: Option<String>,

    /// Flag booleana (presente ou ausente)
    #[arg(short, long)]
    force: bool,

    /// Múltiplos valores
    #[arg(short = 'f', long = "filter")]
    filtros: Vec<String>,
}

fn main() {
    let args = Args::parse();
    println!("Origem: {}", args.origem);
    if let Some(dest) = args.destino {
        println!("Destino: {}", dest);
    }
    println!("Filtros: {:?}", args.filtros);
}

Execute assim: cargo run -- arquivo.txt dest.txt -f json -f csv --force. O clap coleta múltiplos valores em Vec automaticamente.

Subcomandos

Para aplicações complexas, subcomandos são essenciais:

use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(name = "admin")]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand, Debug)]
enum Commands {
    /// Criar novo usuário
    Create {
        #[arg(short, long)]
        name: String,

        #[arg(short, long)]
        email: String,
    },

    /// Deletar usuário
    Delete {
        #[arg(short, long)]
        id: u32,
    },

    /// Listar todos os usuários
    List {
        #[arg(short, long)]
        verbose: bool,
    },
}

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

    match cli.command {
        Commands::Create { name, email } => {
            println!("Criando usuário: {} ({})", name, email);
        }
        Commands::Delete { id } => {
            println!("Deletando usuário com ID: {}", id);
        }
        Commands::List { verbose } => {
            println!("Listando usuários (verbose={})", verbose);
        }
    }
}

Use assim: cargo run -- create --name "João" --email "joao@email.com". Cada subcomando tem seus próprios argumentos e help text.

Validação, Valores Padrão e Comportamentos Avançados

Validação e Restrições

O clap oferece várias formas de validar entrada:

use clap::Parser;

#[derive(Parser, Debug)]
struct Args {
    /// Número entre 1 e 100
    #[arg(short, long, value_parser = clap::value_parser!(u32).range(1..=100))]
    numero: u32,

    /// Arquivo que deve existir
    #[arg(short, long, value_parser = clap::builder::PathBufValueParser::new())]
    arquivo: std::path::PathBuf,

    /// Valor de um conjunto específico
    #[arg(short, long, value_parser = ["json", "yaml", "toml"])]
    formato: String,

    /// Valor padrão
    #[arg(short, long, default_value = "info")]
    nivel: String,
}

fn main() {
    let args = Args::parse();
    println!("Número: {}, Arquivo: {:?}", args.numero, args.arquivo);
    println!("Formato: {}, Nível: {}", args.formato, args.nivel);
}

Se executar cargo run -- --numero 150, o clap rejeita e exibe um erro claro. O value_parser faz validação automática.

Comportamentos Avançados

Para casos mais complexos, use atributos customizados:

use clap::Parser;

#[derive(Parser, Debug)]
struct Args {
    /// Requer que outra flag esteja presente
    #[arg(short, long, requires = "token")]
    autenticado: bool,

    /// Conflita com outra flag
    #[arg(long, conflicts_with = "output")]
    stdout: bool,

    #[arg(long)]
    output: Option<String>,

    /// Token (obrigatório se 'autenticado' estiver presente)
    #[arg(short, long)]
    token: Option<String>,

    /// Variável de ambiente como fallback
    #[arg(short, long, env = "APP_DEBUG")]
    debug: bool,
}

fn main() {
    let args = Args::parse();
    println!("{:?}", args);
}

Experimente: cargo run -- --autenticado (vai falhar porque precisa de --token). Ou cargo run -- --autenticado --token abc123 (funciona).

Conclusão

Aprendemos que o clap simplifica drasticamente o desenvolvimento de CLIs em Rust, eliminando boilerplate e oferecendo validação robusta out-of-the-box. A abordagem derive é mais legível e manutenível do que construção manual de parsers. E, finalmente, o clap escala bem: de aplicações simples com um único comando até sistemas complexos com múltiplos subcomandos, validações customizadas e comportamentos avançados.

Na prática, 95% dos seus problemas de parsing será resolvido com os conceitos aqui apresentados. O restante está bem documentado na documentação oficial.

Referências


Artigos relacionados