Rust, combinado com a crate clap, permite criar uma ferramenta de linha de comando para validar e consultar CPFs com parsing de argumentos, subcomandos e documentação automática gerada a partir do código. Este guia mostra como construir um CLI com três subcomandos: validação local de dígitos verificadores, consulta à API da CPFHub.io e processamento em lote a partir de arquivo.
Introdução
Ferramentas de linha de comando são extremamente úteis para automação e integração com scripts. Rust, com a crate clap, oferece uma forma elegante de construir CLIs com parsing de argumentos, subcomandos e documentação automática. A documentação oficial da linguagem em doc.rust-lang.org detalha os padrões de projeto assíncronos usados neste tutorial.
Configurando o projeto
Crie o projeto e adicione as dependências ao Cargo.toml:
// Cargo.toml
[package]
name = "cpf-cli"
version = "0.1.0"
edition = "2021"
[dependencies]
clap = { version = "4", features = ["derive"] }
reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
colored = "2"
| Crate | Finalidade |
|---|---|
clap | Parsing de argumentos e subcomandos |
reqwest | Requisições HTTP à API |
serde | Desserialização JSON |
colored | Saída colorida no terminal |
Definindo a interface do CLI
Utilize os derives do clap para declarar os argumentos e subcomandos:
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "cpf-cli")]
#[command(about = "Ferramenta de validação e consulta de CPF")]
#[command(version = "1.0")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Validar os dígitos verificadores de um CPF
Validar {
/// O CPF a ser validado (apenas dígitos ou formatado)
cpf: String,
},
/// Consultar dados de um CPF na API do CPFHub
Consultar {
/// O CPF a ser consultado
cpf: String,
/// Chave de API do CPFHub (ou use CPFHUB_API_KEY)
#[arg(short, long, env = "CPFHUB_API_KEY")]
api_key: String,
},
/// Processar um lote de CPFs a partir de um arquivo
Lote {
/// Caminho do arquivo com CPFs (um por linha)
arquivo: String,
/// Chave de API do CPFHub
#[arg(short, long, env = "CPFHUB_API_KEY")]
api_key: String,
},
}
Implementando a validação local
O subcomando validar verifica os dígitos verificadores sem chamar a API:
use colored::*;
fn validar_cpf(cpf: &str) -> bool {
let digitos: Vec<u32> = cpf.chars()
.filter(|c| c.is_ascii_digit())
.filter_map(|c| c.to_digit(10))
.collect();
if digitos.len() != 11 || digitos.iter().all(|&d| d == digitos[0]) {
return false;
}
let soma: u32 = digitos.iter().take(9)
.enumerate()
.map(|(i, &d)| d * (10 - i as u32))
.sum();
let d1 = if soma % 11 < 2 { 0 } else { 11 - (soma % 11) };
let soma: u32 = digitos.iter().take(10)
.enumerate()
.map(|(i, &d)| d * (11 - i as u32))
.sum();
let d2 = if soma % 11 < 2 { 0 } else { 11 - (soma % 11) };
digitos[9] == d1 && digitos[10] == d2
}
fn executar_validacao(cpf: &str) {
let cpf_limpo: String = cpf.chars().filter(|c| c.is_ascii_digit()).collect();
if cpf_limpo.len() != 11 {
println!("{} CPF deve conter 11 dígitos (recebido: {})",
"ERRO:".red().bold(), cpf_limpo.len());
return;
}
if validar_cpf(&cpf_limpo) {
println!("{} CPF {} possui dígitos verificadores válidos",
"VALIDO:".green().bold(), cpf);
} else {
println!("{} CPF {} possui dígitos verificadores inválidos",
"INVALIDO:".red().bold(), cpf);
}
}
Implementando a consulta à API
O subcomando consultar chama a API do CPFHub e exibe os dados formatados:
use serde::Deserialize;
use reqwest::header::{HeaderMap, HeaderValue};
#[derive(Debug, Deserialize)]
pub struct CpfResponse {
pub success: bool,
pub data: CpfData,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CpfData {
pub cpf: String,
pub name: String,
pub name_upper: String,
pub gender: String,
pub birth_date: String,
pub day: String,
pub month: String,
pub year: String,
}
async fn executar_consulta(cpf: &str, api_key: &str) {
let mut headers = HeaderMap::new();
headers.insert("x-api-key", HeaderValue::from_str(api_key).unwrap());
let client = reqwest::Client::new();
let url = format!("https://api.cpfhub.io/cpf/{}", cpf);
match client.get(&url).headers(headers).send().await {
Ok(response) if response.status().is_success() => {
match response.json::<CpfResponse>().await {
Ok(dados) if dados.success => {
println!("{}", "CPF encontrado!".green().bold());
println!(" {}: {}", "Nome".bold(), dados.data.name);
println!(" {}: {}", "CPF".bold(), dados.data.cpf);
println!(" {}: {}", "Nascimento".bold(), dados.data.birth_date);
println!(" {}: {}", "Gênero".bold(), dados.data.gender);
}
_ => println!("{} CPF não encontrado na base", "AVISO:".yellow().bold()),
}
}
Ok(response) => {
println!("{} API retornou status {}", "ERRO:".red().bold(), response.status());
}
Err(e) => {
println!("{} {}", "ERRO:".red().bold(), e);
}
}
}
Montando o programa principal
Conecte todos os subcomandos no ponto de entrada da aplicação:
#[tokio::main]
async fn main() {
let cli = Cli::parse();
match cli.command {
Commands::Validar { cpf } => {
executar_validacao(&cpf);
}
Commands::Consultar { cpf, api_key } => {
executar_consulta(&cpf, &api_key).await;
}
Commands::Lote { arquivo, api_key } => {
let conteudo = std::fs::read_to_string(&arquivo)
.expect("Não foi possível ler o arquivo");
let cpfs: Vec<&str> = conteudo.lines()
.filter(|l| !l.trim().is_empty())
.collect();
println!("Processando {} CPFs...\n", cpfs.len());
for cpf in cpfs {
executar_consulta(cpf.trim(), &api_key).await;
println!("---");
}
}
}
}
Perguntas frequentes
Por que usar Rust para um CLI de validação de CPF?
Rust gera binários nativos sem dependência de runtime, com startup quase instantânea e consumo mínimo de memória. Para scripts de automação e pipelines CI/CD que processam grandes volumes de CPFs, isso representa vantagem real frente a Python ou Node.js, que exigem interpretador instalado.
Como armazenar a chave de API com segurança no CLI?
A forma recomendada é usar a variável de ambiente CPFHUB_API_KEY, como mostrado no exemplo com #[arg(env = "CPFHUB_API_KEY")]. Isso evita que a chave apareça no histórico do shell ou em logs de processo. Nunca passe a chave como argumento posicional em scripts automatizados.
A API bloqueia requisições quando o limite mensal é atingido?
Não. A API da CPFHub.io não retorna HTTP 429 nem bloqueia consultas ao atingir o limite do plano gratuito. Ela cobra R$0,15 por consulta excedente e continua respondendo normalmente. Para volumes maiores, o plano Pro oferece 1.000 consultas mensais por R$149.
Como processar arquivos com milhares de CPFs sem sobrecarregar a API?
Adicione um sleep entre as chamadas do subcomando lote para respeitar o rate limit do plano (1 req/2s no gratuito, 1 req/s no Pro). Use tokio::time::sleep(Duration::from_millis(500)) após cada chamada no loop de processamento.
Conclusão
Criar um CLI de validação de CPF em Rust com clap resulta em uma ferramenta rápida, com interface profissional e documentação automática. A combinação de subcomandos, variáveis de ambiente e processamento em lote atende tanto uso interativo quanto automação.
Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e use o CLI com dados reais para automatizar a validação de CPF nos seus scripts e pipelines.
CPFHub.io
Pronto para integrar a API?
50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.
Sobre a redação
Redação CPFHub.io
Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.



