rbytes_consulta_cep 0.1.0

Consulta de CEP brasileiro com multi-provedores (ViaCEP, BrasilAPI, etc). Inclui fallback automático, timeout configurável e formatação de endereços.
Documentation
// src/providers/apicep.rs
use crate::{
    error::CepError,
    models::Endereco,
    validacao::{normalizar_cep, formatar_cep} // Importe ambas as funções
};
use reqwest::{Client, StatusCode};
use serde::Deserialize;
use std::time::Duration;

#[derive(Debug, Deserialize)]
struct ApiCepResponse {
    code: String,
    state: String,
    city: String,
    district: String,
    address: String,
    status: u32,
    ok: bool,
    statusText: String,
}

pub async fn consultar(cep: &str) -> Result<Endereco, CepError> {
    let cep_formatado = formatar_cep(cep)?; 
    let url = format!("https://cdn.apicep.com/file/apicep/{}.json", cep_formatado);
    
    let response = Client::new()
        .get(&url)
        .timeout(Duration::from_secs(2))
        .send()
        .await?;

    match response.status() {
        StatusCode::OK => {
            let apicep_response: ApiCepResponse = response.json().await?;
            
            if !apicep_response.ok {
                return Err(CepError::CepNaoEncontrado);
            }

            Ok(Endereco {
                cep: apicep_response.code,
                logradouro: Some(apicep_response.address)
                    .filter(|s| !s.is_empty()),
                complemento: None,
                bairro: Some(apicep_response.district)
                    .filter(|s| !s.is_empty()),
                localidade: apicep_response.city,
                uf: apicep_response.state,
                ibge: None,
                gia: None,
                ddd: None,
                siafi: None,
            })
        },
        StatusCode::BAD_REQUEST => Err(CepError::CepInvalido),
        StatusCode::TOO_MANY_REQUESTS => Err(CepError::RateLimitExcedido("ApiCEP".into())),
        _ => Err(CepError::ProvedorIndisponivel("ApiCEP".into())),
    }
}