Como criar um CLI de validação de CPF em Rust

Construa uma ferramenta de linha de comando em Rust para validar e consultar CPFs usando a crate clap e a API do CPFHub.

Redação CPFHub.io
Redação CPFHub.io
··6 min de leitura
Como criar um CLI de validação de CPF em Rust

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"
CrateFinalidade
clapParsing de argumentos e subcomandos
reqwestRequisições HTTP à API
serdeDesserialização JSON
coloredSaí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.

Redação CPFHub.io

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.

WhatsAppFale conosco via WhatsApp