1use std::cmp;
8use std::cmp::Ordering;
9
10const CNPJ_LENGTH: usize = 14;
11
12fn get_separator(x: usize) -> &'static str {
13 match x {
14 2 | 5 => ".",
15 8 => "/",
16 12 => "-",
17 _ => "",
18 }
19}
20
21pub fn format(cnpj: &str) -> String {
46 let cnpj = cnpj.matches(char::is_numeric).collect::<Vec<_>>();
47
48 let mut cnpj_with_mask: String = String::from("");
49
50 for (i, n) in cnpj
51 .iter()
52 .enumerate()
53 .take(cmp::min(cnpj.len(), CNPJ_LENGTH))
54 {
55 cnpj_with_mask.push_str(get_separator(i));
56 cnpj_with_mask.push_str(n);
57 }
58
59 cnpj_with_mask
60}
61
62pub fn reserved_numbers() -> Vec<String> {
63 vec![
64 String::from("00000000000000"),
65 String::from("11111111111111"),
66 String::from("22222222222222"),
67 String::from("33333333333333"),
68 String::from("44444444444444"),
69 String::from("55555555555555"),
70 String::from("66666666666666"),
71 String::from("77777777777777"),
72 String::from("88888888888888"),
73 String::from("99999999999999"),
74 ]
75}
76
77fn check_sum(cnpj: &[&str], factors: Vec<u32>) -> u32 {
78 let mut sum: u32 = 0;
79 for x in 0..factors.len() {
80 sum += cnpj[x].parse::<u32>().unwrap() * factors[x];
81 }
82
83 let mod_11 = sum % 11;
84
85 match mod_11.cmp(&2) {
86 Ordering::Less => 0,
87 _ => 11 - mod_11,
88 }
89}
90
91fn validate(cnpj: String) -> bool {
92 let cnpj = cnpj.matches(char::is_numeric).collect::<Vec<_>>();
93
94 let factors = vec![5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
95 let digito_1 = check_sum(&cnpj, factors);
96
97 let factors = vec![6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
98 let digito_2 = check_sum(&cnpj, factors);
99
100 digito_1 == cnpj[CNPJ_LENGTH - 2].parse::<u32>().unwrap()
101 && digito_2 == cnpj[CNPJ_LENGTH - 1].parse::<u32>().unwrap()
102}
103
104pub fn is_valid(cnpj: &str) -> bool {
116 if cnpj.matches(char::is_lowercase).count() > 0 || cnpj.matches(char::is_uppercase).count() > 0
117 {
118 return false;
119 }
120
121 let cnpj = cnpj.matches(char::is_numeric).collect::<Vec<_>>().concat();
122
123 cnpj.len() == CNPJ_LENGTH
124 && !reserved_numbers().contains(&cnpj)
125 && !cnpj.is_empty()
126 && validate(cnpj)
127}
128
129#[cfg(test)]
130mod test_is_valid {
131 use super::*;
132
133 #[test]
134 fn should_return_false_when_it_is_on_reserved_numbers() {
135 for reserved_number in reserved_numbers() {
136 assert!(!is_valid(&reserved_number));
137 }
138 }
139
140 #[test]
141 fn should_return_false_when_is_a_empty_string() {
142 assert!(!is_valid(""));
143 }
144
145 #[test]
146 fn should_return_false_when_dont_match_with_cnpj_length() {
147 assert!(!is_valid("12312312312"));
148 }
149
150 #[test]
151 fn should_return_false_when_contains_only_letters_or_special_characters() {
152 assert!(!is_valid("ababcabcabcdab"));
153 }
154
155 #[test]
156 fn should_return_false_when_is_a_cnpj_invalid_test_numbers_with_letters() {
157 assert!(!is_valid("6ad0.t391.9asd47/0ad001-00"));
158 }
159
160 #[test]
161 fn should_return_false_when_is_a_cnpj_invalid() {
162 assert!(!is_valid("11257245286531"));
163 }
164
165 #[test]
166 fn should_return_true_when_is_a_valid_cnpj_without_mask() {
167 assert!(is_valid("13723705000189"));
168 }
169
170 #[test]
171 fn should_return_true_when_is_a_cnpj_valid_with_mask() {
172 assert!(is_valid("60.391.947/0001-00"));
173 }
174}
175
176#[cfg(test)]
177mod test_format {
178 use super::*;
179
180 #[test]
181 fn should_format_cnpj_with_mask() {
182 assert_eq!(format(""), "");
183 assert_eq!(format("4"), "4");
184 assert_eq!(format("46"), "46");
185 assert_eq!(format("468"), "46.8");
186 assert_eq!(format("4684"), "46.84");
187 assert_eq!(format("46843"), "46.843");
188 assert_eq!(format("468434"), "46.843.4");
189 assert_eq!(format("4684348"), "46.843.48");
190 assert_eq!(format("46843485"), "46.843.485");
191 assert_eq!(format("468434850"), "46.843.485/0");
192 assert_eq!(format("4684348500"), "46.843.485/00");
193 assert_eq!(format("46843485000"), "46.843.485/000");
194 assert_eq!(format("468434850001"), "46.843.485/0001");
195 assert_eq!(format("4684348500018"), "46.843.485/0001-8");
196 assert_eq!(format("46843485000186"), "46.843.485/0001-86");
197 }
198
199 #[test]
200 fn should_not_add_digits_after_the_cnpj_length() {
201 assert_eq!(format("468434850001860000000000"), "46.843.485/0001-86");
202 }
203
204 #[test]
205 fn should_remove_all_non_numeric_characters() {
206 assert_eq!(format("46.?ABC843.485/0001-86abc"), "46.843.485/0001-86");
207 }
208}