Rust

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

8 min de leitura

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

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

<h2>Introdução ao Clap: Por Que Usar?</h2>

<p>O <code>clap</code> é a crate mais popular do Rust para parsing de argumentos de linha de comando. Diferentemente de fazer parsing manual com <code>std::env::args()</code>, 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.</p>

<p>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.</p>

<h2>Instalação e Configuração Básica</h2>

<h3>Setup Inicial</h3>

<p>Comece adicionando o clap ao seu <code>Cargo.toml</code>:</p>

<pre><code class="language-toml">[dependencies]

clap = { version = &quot;4.4&quot;, features = [&quot;derive&quot;] }</code></pre>

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

<pre><code class="language-rust">use clap::Parser;

#[derive(Parser, Debug)]

#[command(name = &quot;MeuApp&quot;)]

#[command(about = &quot;Uma ferramenta CLI demonstrativa&quot;, 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!(&quot;Arquivo: {}&quot;, args.arquivo);

println!(&quot;Verbose: {}&quot;, args.verbose);

}</code></pre>

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

<h2>Argumentos, Opções e Subcomandos</h2>

<h3>Argumentos vs Opções</h3>

<p>Argumentos posicionais (sem <code>--</code> ou <code>-</code>) e opções (com <code>--</code> ou <code>-</code>) funcionam diferentemente no clap:</p>

<pre><code class="language-rust">use clap::Parser;

#[derive(Parser, Debug)]

struct Args {

/// Argumento posicional obrigatório

#[arg(value_name = &quot;ORIGEM&quot;)]

origem: String,

/// Argumento posicional opcional

#[arg(value_name = &quot;DESTINO&quot;)]

destino: Option&lt;String&gt;,

/// Opção com valor

#[arg(short = &#039;o&#039;, long = &quot;output&quot;)]

output: Option&lt;String&gt;,

/// Flag booleana (presente ou ausente)

#[arg(short, long)]

force: bool,

/// Múltiplos valores

#[arg(short = &#039;f&#039;, long = &quot;filter&quot;)]

filtros: Vec&lt;String&gt;,

}

fn main() {

let args = Args::parse();

println!(&quot;Origem: {}&quot;, args.origem);

if let Some(dest) = args.destino {

println!(&quot;Destino: {}&quot;, dest);

}

println!(&quot;Filtros: {:?}&quot;, args.filtros);

}</code></pre>

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

<h3>Subcomandos</h3>

<p>Para aplicações complexas, subcomandos são essenciais:</p>

<pre><code class="language-rust">use clap::{Parser, Subcommand};

#[derive(Parser)]

#[command(name = &quot;admin&quot;)]

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 } =&gt; {

println!(&quot;Criando usuário: {} ({})&quot;, name, email);

}

Commands::Delete { id } =&gt; {

println!(&quot;Deletando usuário com ID: {}&quot;, id);

}

Commands::List { verbose } =&gt; {

println!(&quot;Listando usuários (verbose={})&quot;, verbose);

}

}

}</code></pre>

<p>Use assim: <code>cargo run -- create --name &quot;João&quot; --email &quot;joao@email.com&quot;</code>. Cada subcomando tem seus próprios argumentos e help text.</p>

<h2>Validação, Valores Padrão e Comportamentos Avançados</h2>

<h3>Validação e Restrições</h3>

<p>O clap oferece várias formas de validar entrada:</p>

<pre><code class="language-rust">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 = [&quot;json&quot;, &quot;yaml&quot;, &quot;toml&quot;])]

formato: String,

/// Valor padrão

#[arg(short, long, default_value = &quot;info&quot;)]

nivel: String,

}

fn main() {

let args = Args::parse();

println!(&quot;Número: {}, Arquivo: {:?}&quot;, args.numero, args.arquivo);

println!(&quot;Formato: {}, Nível: {}&quot;, args.formato, args.nivel);

}</code></pre>

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

<h3>Comportamentos Avançados</h3>

<p>Para casos mais complexos, use atributos customizados:</p>

<pre><code class="language-rust">use clap::Parser;

#[derive(Parser, Debug)]

struct Args {

/// Requer que outra flag esteja presente

#[arg(short, long, requires = &quot;token&quot;)]

autenticado: bool,

/// Conflita com outra flag

#[arg(long, conflicts_with = &quot;output&quot;)]

stdout: bool,

#[arg(long)]

output: Option&lt;String&gt;,

/// Token (obrigatório se &#039;autenticado&#039; estiver presente)

#[arg(short, long)]

token: Option&lt;String&gt;,

/// Variável de ambiente como fallback

#[arg(short, long, env = &quot;APP_DEBUG&quot;)]

debug: bool,

}

fn main() {

let args = Args::parse();

println!(&quot;{:?}&quot;, args);

}</code></pre>

<p>Experimente: <code>cargo run -- --autenticado</code> (vai falhar porque precisa de <code>--token</code>). Ou <code>cargo run -- --autenticado --token abc123</code> (funciona).</p>

<h2>Conclusão</h2>

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

<p>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.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://docs.rs/clap/latest/clap/" target="_blank" rel="noopener noreferrer">Documentação oficial do clap</a></li>

<li><a href="https://docs.rs/clap/latest/clap/_derive/index.html" target="_blank" rel="noopener noreferrer">Guia de Derive API do clap</a></li>

<li><a href="https://doc.rust-lang.org/book/ch12-00-an-io-project.html" target="_blank" rel="noopener noreferrer">Rust Book - Building a Command Line Program</a></li>

<li><a href="https://github.com/clap-rs/clap/tree/master/examples" target="_blank" rel="noopener noreferrer">Exemplos completos do clap no GitHub</a></li>

<li><a href="https://rust-cli.github.io/book/" target="_blank" rel="noopener noreferrer">The Rust CLI Book</a></li>

</ul>

Comentários

Mais em Rust

Construindo APIs REST com Axum em Rust: Do Básico ao Avançado
Construindo APIs REST com Axum em Rust: Do Básico ao Avançado

Introdução ao Axum e sua Arquitetura Axum é um framework web moderno construí...

Boas Práticas de Features e Compilação Condicional em Rust com Cargo para Times Ágeis
Boas Práticas de Features e Compilação Condicional em Rust com Cargo para Times Ágeis

Features em Rust: O Que São e Por Que Importam As features (características)...

Guia Completo de Criando Tipos de Erro Customizados em Rust
Guia Completo de Criando Tipos de Erro Customizados em Rust

Por que Customizar Tipos de Erro em Rust? Em Rust, tratamento de erros é uma...