Noções Básicas de CLI
10 exemplos para você começar com CLIs Rust: 7 básicos e 3 intermediários.
Pré-requisitos
- Rust 1.97.0 (edição 2024)
cargo add clap --features derive anyhowpara a maioria dos exemplos
Exemplos Básicos
1. Ponto de Entrada Mínimo do Binário
Toda CLI começa com uma main que retorna um resultado.
fn main() -> anyhow::Result<()> {
println!("Olá do meu tool");
Ok(())
}- Retorna
Resultpara que erros se propaguem de forma limpa - Usa
anyhowpara tratamento de erros em nível de aplicação - Mantém
mainenxuta; delega a lógica para funções de biblioteca - Habilita o operador
?para saída antecipada com contexto
Relacionado: Relatório de Erros - erros amigáveis de CLI
2. Lendo Argumentos Posicionais
std::env::args funciona para as ferramentas mais simples.
use std::env;
fn main() -> anyhow::Result<()> {
let name = env::args().nth(1)
.ok_or_else(|| anyhow::anyhow!("uso: greet NOME"))?;
println!("Olá, {name}!");
Ok(())
}args().nth(0)é o nome do binário- Sai com um código diferente de zero em caso de falha (
anyhowfaz isso automaticamente) - Bom para protótipos; mude para
clappara ferramentas reais - Documenta o uso em mensagens de stderr
Relacionado: clap - análise adequada de argumentos
3. Códigos de Saída Significativos
Scripts shell e CI verificam $? após seu comando.
use std::process::ExitCode;
fn main() -> ExitCode {
match run() {
Ok(()) => ExitCode::SUCCESS,
Err(e) if e.is::<std::io::Error>() => ExitCode::from(2),
Err(_) => ExitCode::FAILURE,
}
}0significa sucesso; códigos distintos para falhas distintas- Documenta os códigos de saída em
--help - Imprime erros em stderr, dados em stdout
- Bibliotecas devem retornar
Result; binários mapeiam para códigos de saída
4. stdin, stdout e stderr
CLIs compõem via pipes quando stdout permanece limpo.
use std::io::{self, BufRead};
fn main() -> anyhow::Result<()> {
let stdin = io::stdin();
for line in stdin.lock().lines() {
println!("{}", line?.to_uppercase());
}
Ok(())
}stdoutpara saída de dados (pipeable)stderrpara logs e diagnósticos- Habilita
cat file.txt | mytool | other - Flags verbosas devem registrar em stderr
5. Subcomandos
Organiza verbos como git commit ou cargo build.
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "items")]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Add { name: String },
Remove { id: u64 },
}
fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
match cli.command {
Command::Add { name } => println!("adicionado {name}"),
Command::Remove { id } => println!("removido {id}"),
}
Ok(())
}- Um subcomando por verbo voltado ao usuário
- Cada subcomando recebe suas próprias flags e ajuda
clapgera--helppor subcomando- Base para ferramentas com múltiplos recursos
Relacionado: clap - análise baseada em derive
6. Variáveis de Ambiente
Aplicativos 12-factor leem configuração do ambiente.
use std::env;
fn api_key() -> anyhow::Result<String> {
env::var("API_KEY").map_err(|_| anyhow::anyhow!("defina API_KEY"))
}- Nunca imprima segredos em stdout
- Flags devem substituir variáveis de ambiente (documente a precedência)
- Use
RUST_LOGpara rastreamento em CLIs de produção - Documente variáveis necessárias em
--help
Relacionado: Configuração e Ambiente - camadas de configuração
7. Divisão Biblioteca + Binário
Coloque a lógica em src/lib.rs, mantenha src/main.rs como um wrapper fino.
# Cargo.toml
[lib]
name = "mytool"
path = "src/lib.rs"
[[bin]]
name = "mytool"
path = "src/main.rs"// src/lib.rs
pub fn process(input: &str) -> String {
input.to_uppercase()
}- Teste unitário da biblioteca sem iniciar um processo
- Reutilize a lógica em testes de integração e benchmarks
mainapenas analisa argumentos e chama funções da biblioteca- Padrão padrão para CLIs Rust de produção
Exemplos Intermediários
8. Saída Colorida
Use colored ou owo-colors para mensagens voltadas ao usuário.
use colored::Colorize;
fn main() {
eprintln!("{}", "erro: arquivo não encontrado".red().bold());
println!("{}", "concluído".green());
}- Respeite
NO_COLORe--no-color - Colora avisos em stderr; mantenha stdout simples para piping
- Desabilite a cor quando stdout não for um TTY
- Veja a página de saída do terminal para barras de progresso
Relacionado: Saída do Terminal - indicatif e estilização
9. Progresso para Operações Longas
Mostre feedback quando o trabalho levar mais de um segundo.
use indicatif::{ProgressBar, ProgressStyle};
fn main() {
let bar = ProgressBar::new(100);
bar.set_style(ProgressStyle::with_template(
"[{bar:40}] {pos}/{len} {msg}",
).unwrap());
for i in 0..100 {
bar.set_message(format!("item {i}"));
bar.inc(1);
}
bar.finish_with_message("completo");
}- Barras de progresso vão para stderr
- Use spinners para tarefas de duração desconhecida
- Desabilite o progresso quando
--quietou não-TTY - Emparelhe com tracing para logs do lado do servidor
Relacionado: Saída do Terminal - guia completo
10. Empacotar e Distribuir
Envie um binário global com cargo install ou artefatos de release.
[package]
name = "mytool"
version = "0.1.0"
edition = "2024"
[[bin]]
name = "mytool"
path = "src/main.rs"cargo install --path .
mytool --help- Fixe a versão do Rust em
rust-toolchain.tomlpara builds reproduzíveis - Use
cargo-distpara arquivos de release multiplataforma - Publique em crates.io ou GitHub Releases
- Documente os passos de instalação no README
Relacionado: Empacotamento e Distribuição - cargo-dist
Versões da Pilha: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4 e Polars 0.46+.