1use crate::error::Error;
4
5pub fn formatar_cnpj(cnpj: &str) -> String {
6 if cnpj.len() != 14 {
7 return cnpj.to_string();
8 }
9 format!(
10 "{}.{}.{}/{}-{}",
11 &cnpj[0..2],
12 &cnpj[2..5],
13 &cnpj[5..8],
14 &cnpj[8..12],
15 &cnpj[12..14]
16 )
17}
18
19pub fn formatar_cpf(cpf: &str) -> String {
20 if cpf.len() != 11 {
21 return cpf.to_string();
22 }
23 format!(
24 "{}.{}.{}-{}",
25 &cpf[0..3],
26 &cpf[3..6],
27 &cpf[6..9],
28 &cpf[9..11]
29 )
30}
31
32pub fn formatar_cep(cep: &str) -> String {
33 if cep.len() != 8 {
34 return cep.to_string();
35 }
36 format!("{}-{}", &cep[0..5], &cep[5..8])
37}
38
39pub fn validar_cnpj(cnpj: &str) -> bool {
40 if cnpj.len() != 14 || !cnpj.chars().all(|c| c.is_ascii_digit()) {
41 return false;
42 }
43 if cnpj.chars().all(|c| c == cnpj.chars().next().unwrap()) {
44 return false;
45 }
46
47 let numeros: Vec<u32> = cnpj.chars().map(|c| c.to_digit(10).unwrap()).collect();
48
49 let pesos1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
50 let soma1: u32 = numeros[..12]
51 .iter()
52 .zip(pesos1.iter())
53 .map(|(a, b)| a * b)
54 .sum();
55 let resto1 = soma1 % 11;
56 let digito1 = if resto1 < 2 { 0 } else { 11 - resto1 };
57 if numeros[12] != digito1 {
58 return false;
59 }
60
61 let pesos2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
62 let soma2: u32 = numeros[..13]
63 .iter()
64 .zip(pesos2.iter())
65 .map(|(a, b)| a * b)
66 .sum();
67 let resto2 = soma2 % 11;
68 let digito2 = if resto2 < 2 { 0 } else { 11 - resto2 };
69 numeros[13] == digito2
70}
71
72pub fn validar_cpf(cpf: &str) -> bool {
73 if cpf.len() != 11 || !cpf.chars().all(|c| c.is_ascii_digit()) {
74 return false;
75 }
76 if cpf.chars().all(|c| c == cpf.chars().next().unwrap()) {
77 return false;
78 }
79
80 let numeros: Vec<u32> = cpf.chars().map(|c| c.to_digit(10).unwrap()).collect();
81
82 let pesos1 = [10, 9, 8, 7, 6, 5, 4, 3, 2];
83 let soma1: u32 = numeros[..9]
84 .iter()
85 .zip(pesos1.iter())
86 .map(|(a, b)| a * b)
87 .sum();
88 let resto1 = soma1 % 11;
89 let digito1 = if resto1 < 2 { 0 } else { 11 - resto1 };
90 if numeros[9] != digito1 {
91 return false;
92 }
93
94 let pesos2 = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2];
95 let soma2: u32 = numeros[..10]
96 .iter()
97 .zip(pesos2.iter())
98 .map(|(a, b)| a * b)
99 .sum();
100 let resto2 = soma2 % 11;
101 let digito2 = if resto2 < 2 { 0 } else { 11 - resto2 };
102 numeros[10] == digito2
103}
104
105pub fn calcular_digito_verificador(chave: &str) -> u8 {
106 let pesos = [2, 3, 4, 5, 6, 7, 8, 9];
107 let mut soma = 0;
108 let mut peso_idx = 0;
109
110 for digito in chave.chars().rev() {
111 let valor = digito.to_digit(10).unwrap() as u32;
112 soma += valor * pesos[peso_idx] as u32;
113 peso_idx = (peso_idx + 1) % 8;
114 }
115
116 let resto = soma % 11;
117 if resto == 0 || resto == 1 {
118 0
119 } else {
120 (11 - resto) as u8
121 }
122}
123
124pub fn gerar_chave_acesso(
125 cnpj: &str,
126 estado: u8,
127 serie: u16,
128 numero_nfe: u32,
129 tipo_emi: u8,
130 codigo_numerico: u32,
131) -> Result<String, Error> {
132 if cnpj.len() != 14 {
133 return Err(Error::InvalidCnpj);
134 }
135
136 let estado_str = format!("{:02}", estado);
137 let ano_mes = chrono::Utc::now().format("%y%m").to_string();
138 let modelo = "55";
139 let serie_str = format!("{:03}", serie);
140 let numero_str = format!("{:09}", numero_nfe);
141 let tipo_emi_str = format!("{}", tipo_emi);
142 let codigo_num_str = format!("{:08}", codigo_numerico);
143
144 let base: String = format!(
145 "{}{}{}{}{}{}{}{}",
146 estado_str, ano_mes, cnpj, modelo, serie_str, numero_str, tipo_emi_str, codigo_num_str
147 );
148
149 let dv = calcular_digito_verificador(&base);
150 Ok(format!("{}{}", base, dv))
151}
152
153pub fn formatar_chave_acesso(chave: &str) -> String {
154 if chave.len() != 44 {
155 return chave.to_string();
156 }
157 format!(
158 "{}.{}.{}.{}.{}.{}.{}",
159 &chave[0..2],
160 &chave[2..6],
161 &chave[6..20],
162 &chave[20..22],
163 &chave[22..25],
164 &chave[25..34],
165 &chave[34..44]
166 )
167}
168
169pub fn validar_chave_acesso(chave: &str) -> bool {
170 if chave.len() != 44 || !chave.chars().all(|c| c.is_ascii_digit()) {
171 return false;
172 }
173 let base = &chave[..43];
174 let dv_recebido: u8 = chave[43..44].parse().unwrap_or(0);
175 dv_recebido == calcular_digito_verificador(base)
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn test_formatar_cnpj() {
184 assert_eq!(formatar_cnpj("12345678000199"), "12.345.678/0001-99");
185 }
186
187 #[test]
188 fn test_validar_cnpj_valido() {
189 assert!(validar_cnpj("12345678000195"));
190 assert!(validar_cnpj("99999999999962"));
191 }
192
193 #[test]
194 fn test_validar_cnpj_invalido() {
195 assert!(!validar_cnpj("12345678000198"));
196 assert!(!validar_cnpj("11111111111111"));
197 }
198
199 #[test]
200 fn test_validar_cpf_valido() {
201 assert!(validar_cpf("12345678909"));
202 }
203
204 #[test]
205 fn test_calcular_digito_verificador() {
206 let chave = "4215089999999999999955001000000001123456789";
207 let dv = calcular_digito_verificador(chave);
208 assert!(dv <= 9);
209 }
210
211 #[test]
212 fn test_validar_chave_acesso() {
213 let base = "4215089999999999999955001000000001123456789";
214 let dv = calcular_digito_verificador(base);
215 let chave = format!("{}{}", base, dv);
216 assert!(validar_chave_acesso(&chave));
217 }
218}