use crate::error::Error;
pub fn formatar_cnpj(cnpj: &str) -> String {
if cnpj.len() != 14 {
return cnpj.to_string();
}
format!(
"{}.{}.{}/{}-{}",
&cnpj[0..2],
&cnpj[2..5],
&cnpj[5..8],
&cnpj[8..12],
&cnpj[12..14]
)
}
pub fn formatar_cpf(cpf: &str) -> String {
if cpf.len() != 11 {
return cpf.to_string();
}
format!(
"{}.{}.{}-{}",
&cpf[0..3],
&cpf[3..6],
&cpf[6..9],
&cpf[9..11]
)
}
pub fn formatar_cep(cep: &str) -> String {
if cep.len() != 8 {
return cep.to_string();
}
format!("{}-{}", &cep[0..5], &cep[5..8])
}
pub fn validar_cnpj(cnpj: &str) -> bool {
if cnpj.len() != 14 || !cnpj.chars().all(|c| c.is_ascii_digit()) {
return false;
}
if cnpj.chars().all(|c| c == cnpj.chars().next().unwrap()) {
return false;
}
let numeros: Vec<u32> = cnpj.chars().map(|c| c.to_digit(10).unwrap()).collect();
let pesos1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
let soma1: u32 = numeros[..12]
.iter()
.zip(pesos1.iter())
.map(|(a, b)| a * b)
.sum();
let resto1 = soma1 % 11;
let digito1 = if resto1 < 2 { 0 } else { 11 - resto1 };
if numeros[12] != digito1 {
return false;
}
let pesos2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
let soma2: u32 = numeros[..13]
.iter()
.zip(pesos2.iter())
.map(|(a, b)| a * b)
.sum();
let resto2 = soma2 % 11;
let digito2 = if resto2 < 2 { 0 } else { 11 - resto2 };
numeros[13] == digito2
}
pub fn validar_cpf(cpf: &str) -> bool {
if cpf.len() != 11 || !cpf.chars().all(|c| c.is_ascii_digit()) {
return false;
}
if cpf.chars().all(|c| c == cpf.chars().next().unwrap()) {
return false;
}
let numeros: Vec<u32> = cpf.chars().map(|c| c.to_digit(10).unwrap()).collect();
let pesos1 = [10, 9, 8, 7, 6, 5, 4, 3, 2];
let soma1: u32 = numeros[..9]
.iter()
.zip(pesos1.iter())
.map(|(a, b)| a * b)
.sum();
let resto1 = soma1 % 11;
let digito1 = if resto1 < 2 { 0 } else { 11 - resto1 };
if numeros[9] != digito1 {
return false;
}
let pesos2 = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2];
let soma2: u32 = numeros[..10]
.iter()
.zip(pesos2.iter())
.map(|(a, b)| a * b)
.sum();
let resto2 = soma2 % 11;
let digito2 = if resto2 < 2 { 0 } else { 11 - resto2 };
numeros[10] == digito2
}
pub fn calcular_digito_verificador(chave: &str) -> u8 {
let pesos = [2, 3, 4, 5, 6, 7, 8, 9];
let mut soma = 0;
let mut peso_idx = 0;
for digito in chave.chars().rev() {
let valor = digito.to_digit(10).unwrap() as u32;
soma += valor * pesos[peso_idx] as u32;
peso_idx = (peso_idx + 1) % 8;
}
let resto = soma % 11;
if resto == 0 || resto == 1 {
0
} else {
(11 - resto) as u8
}
}
pub fn gerar_chave_acesso(
cnpj: &str,
estado: u8,
serie: u16,
numero_nfe: u32,
tipo_emi: u8,
codigo_numerico: u32,
) -> Result<String, Error> {
if cnpj.len() != 14 {
return Err(Error::InvalidCnpj);
}
let estado_str = format!("{:02}", estado);
let ano_mes = chrono::Utc::now().format("%y%m").to_string();
let modelo = "55";
let serie_str = format!("{:03}", serie);
let numero_str = format!("{:09}", numero_nfe);
let tipo_emi_str = format!("{}", tipo_emi);
let codigo_num_str = format!("{:08}", codigo_numerico);
let base: String = format!(
"{}{}{}{}{}{}{}{}",
estado_str, ano_mes, cnpj, modelo, serie_str, numero_str, tipo_emi_str, codigo_num_str
);
let dv = calcular_digito_verificador(&base);
Ok(format!("{}{}", base, dv))
}
pub fn formatar_chave_acesso(chave: &str) -> String {
if chave.len() != 44 {
return chave.to_string();
}
format!(
"{}.{}.{}.{}.{}.{}.{}",
&chave[0..2],
&chave[2..6],
&chave[6..20],
&chave[20..22],
&chave[22..25],
&chave[25..34],
&chave[34..44]
)
}
pub fn validar_chave_acesso(chave: &str) -> bool {
if chave.len() != 44 || !chave.chars().all(|c| c.is_ascii_digit()) {
return false;
}
let base = &chave[..43];
let dv_recebido: u8 = chave[43..44].parse().unwrap_or(0);
dv_recebido == calcular_digito_verificador(base)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_formatar_cnpj() {
assert_eq!(formatar_cnpj("12345678000199"), "12.345.678/0001-99");
}
#[test]
fn test_validar_cnpj_valido() {
assert!(validar_cnpj("12345678000195"));
assert!(validar_cnpj("99999999999962"));
}
#[test]
fn test_validar_cnpj_invalido() {
assert!(!validar_cnpj("12345678000198"));
assert!(!validar_cnpj("11111111111111"));
}
#[test]
fn test_validar_cpf_valido() {
assert!(validar_cpf("12345678909"));
}
#[test]
fn test_calcular_digito_verificador() {
let chave = "4215089999999999999955001000000001123456789";
let dv = calcular_digito_verificador(chave);
assert!(dv <= 9);
}
#[test]
fn test_validar_chave_acesso() {
let base = "4215089999999999999955001000000001123456789";
let dv = calcular_digito_verificador(base);
let chave = format!("{}{}", base, dv);
assert!(validar_chave_acesso(&chave));
}
}