use std::cmp;
use std::cmp::Ordering;
const CNPJ_LENGTH: usize = 14;
fn get_separator(x: usize) -> &'static str {
match x {
2 | 5 => ".",
8 => "/",
12 => "-",
_ => "",
}
}
pub fn format(cnpj: &str) -> String {
let cnpj = cnpj.matches(char::is_numeric).collect::<Vec<_>>();
let mut cnpj_with_mask: String = String::from("");
for (i, n) in cnpj
.iter()
.enumerate()
.take(cmp::min(cnpj.len(), CNPJ_LENGTH))
{
cnpj_with_mask.push_str(get_separator(i));
cnpj_with_mask.push_str(n);
}
cnpj_with_mask
}
pub fn reserved_numbers() -> Vec<String> {
vec![
String::from("00000000000000"),
String::from("11111111111111"),
String::from("22222222222222"),
String::from("33333333333333"),
String::from("44444444444444"),
String::from("55555555555555"),
String::from("66666666666666"),
String::from("77777777777777"),
String::from("88888888888888"),
String::from("99999999999999"),
]
}
fn check_sum(cnpj: &[&str], factors: Vec<u32>) -> u32 {
let mut sum: u32 = 0;
for x in 0..factors.len() {
sum += cnpj[x].parse::<u32>().unwrap() * factors[x];
}
let mod_11 = sum % 11;
match mod_11.cmp(&2) {
Ordering::Less => 0,
_ => 11 - mod_11,
}
}
fn validate(cnpj: String) -> bool {
let cnpj = cnpj.matches(char::is_numeric).collect::<Vec<_>>();
let factors = vec![5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
let digito_1 = check_sum(&cnpj, factors);
let factors = vec![6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
let digito_2 = check_sum(&cnpj, factors);
digito_1 == cnpj[CNPJ_LENGTH - 2].parse::<u32>().unwrap()
&& digito_2 == cnpj[CNPJ_LENGTH - 1].parse::<u32>().unwrap()
}
pub fn is_valid(cnpj: &str) -> bool {
if cnpj.matches(char::is_lowercase).count() > 0 || cnpj.matches(char::is_uppercase).count() > 0
{
return false;
}
let cnpj = cnpj.matches(char::is_numeric).collect::<Vec<_>>().concat();
cnpj.len() == CNPJ_LENGTH
&& !reserved_numbers().contains(&cnpj)
&& !cnpj.is_empty()
&& validate(cnpj)
}
#[cfg(test)]
mod test_is_valid {
use super::*;
#[test]
fn should_return_false_when_it_is_on_reserved_numbers() {
for reserved_number in reserved_numbers() {
assert!(!is_valid(&reserved_number));
}
}
#[test]
fn should_return_false_when_is_a_empty_string() {
assert!(!is_valid(""));
}
#[test]
fn should_return_false_when_dont_match_with_cnpj_length() {
assert!(!is_valid("12312312312"));
}
#[test]
fn should_return_false_when_contains_only_letters_or_special_characters() {
assert!(!is_valid("ababcabcabcdab"));
}
#[test]
fn should_return_false_when_is_a_cnpj_invalid_test_numbers_with_letters() {
assert!(!is_valid("6ad0.t391.9asd47/0ad001-00"));
}
#[test]
fn should_return_false_when_is_a_cnpj_invalid() {
assert!(!is_valid("11257245286531"));
}
#[test]
fn should_return_true_when_is_a_valid_cnpj_without_mask() {
assert!(is_valid("13723705000189"));
}
#[test]
fn should_return_true_when_is_a_cnpj_valid_with_mask() {
assert!(is_valid("60.391.947/0001-00"));
}
}
#[cfg(test)]
mod test_format {
use super::*;
#[test]
fn should_format_cnpj_with_mask() {
assert_eq!(format(""), "");
assert_eq!(format("4"), "4");
assert_eq!(format("46"), "46");
assert_eq!(format("468"), "46.8");
assert_eq!(format("4684"), "46.84");
assert_eq!(format("46843"), "46.843");
assert_eq!(format("468434"), "46.843.4");
assert_eq!(format("4684348"), "46.843.48");
assert_eq!(format("46843485"), "46.843.485");
assert_eq!(format("468434850"), "46.843.485/0");
assert_eq!(format("4684348500"), "46.843.485/00");
assert_eq!(format("46843485000"), "46.843.485/000");
assert_eq!(format("468434850001"), "46.843.485/0001");
assert_eq!(format("4684348500018"), "46.843.485/0001-8");
assert_eq!(format("46843485000186"), "46.843.485/0001-86");
}
#[test]
fn should_not_add_digits_after_the_cnpj_length() {
assert_eq!(format("468434850001860000000000"), "46.843.485/0001-86");
}
#[test]
fn should_remove_all_non_numeric_characters() {
assert_eq!(format("46.?ABC843.485/0001-86abc"), "46.843.485/0001-86");
}
}