use rand::Rng;
const SIZE: usize = 14;
pub fn remove_symbols(dirty: &str) -> String {
dirty
.chars()
.filter(|c| *c != '.' && *c != '/' && *c != '-')
.collect()
}
pub fn format_cnpj(cnpj: &str) -> Option<String> {
if !is_valid(cnpj) {
return None;
}
Some(format!(
"{}.{}.{}/{}-{}",
&cnpj[0..2],
&cnpj[2..5],
&cnpj[5..8],
&cnpj[8..12],
&cnpj[12..14]
))
}
pub fn validate(cnpj: &str) -> bool {
if !cnpj.chars().all(|c| c.is_ascii_digit()) || cnpj.len() != SIZE {
return false;
}
if cnpj.chars().all(|c| c == cnpj.chars().next().unwrap()) {
return false;
}
let digit_13 = hashdigit(cnpj, 13);
let digit_14 = hashdigit(cnpj, 14);
cnpj.chars().nth(12).unwrap().to_digit(10).unwrap() == digit_13 as u32
&& cnpj.chars().nth(13).unwrap().to_digit(10).unwrap() == digit_14 as u32
}
pub fn is_valid(cnpj: &str) -> bool {
validate(cnpj)
}
pub fn generate(branch: Option<u32>) -> String {
let mut rng = rand::thread_rng();
let mut branch_num = branch.unwrap_or(1);
branch_num %= 10000;
if branch_num == 0 {
branch_num = 1;
}
let branch_str = format!("{:04}", branch_num);
let base_num = format!("{:08}", rng.gen_range(0..=99999999));
let base = format!("{}{}", base_num, branch_str);
let checksum = compute_checksum(&base);
format!("{}{}", base, checksum)
}
pub fn hashdigit(cnpj: &str, position: usize) -> usize {
let weights: Vec<usize> = if position == 13 {
vec![5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
} else {
vec![6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
};
let sum: usize = cnpj
.chars()
.take(position - 1)
.enumerate()
.map(|(i, c)| c.to_digit(10).unwrap() as usize * weights[i])
.sum();
let remainder = sum % 11;
if remainder < 2 {
0
} else {
11 - remainder
}
}
pub fn compute_checksum(basenum: &str) -> String {
let digit1 = hashdigit(basenum, 13);
let with_digit1 = format!("{}{}", basenum, digit1);
let digit2 = hashdigit(&with_digit1, 14);
format!("{}{}", digit1, digit2)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_remove_symbols() {
assert_eq!(remove_symbols("00000000000"), "00000000000");
assert_eq!(remove_symbols("12.345.678/0001-90"), "12345678000190");
assert_eq!(remove_symbols("134..2435/.-1892.-"), "13424351892");
assert_eq!(remove_symbols("abc1230916*!*&#"), "abc1230916*!*&#");
assert_eq!(
remove_symbols("ab.c1.--.2-3/09.-1-./6/-.*.-!*&#"),
"abc1230916*!*&#"
);
assert_eq!(remove_symbols("/...---.../"), "");
}
#[test]
fn test_format_cnpj() {
assert_eq!(
format_cnpj("03560714000142"),
Some("03.560.714/0001-42".to_string())
);
assert_eq!(
format_cnpj("01838723000127"),
Some("01.838.723/0001-27".to_string())
);
assert_eq!(
format_cnpj("34665388000161"),
Some("34.665.388/0001-61".to_string())
);
assert_eq!(format_cnpj("98765432100100"), None);
assert_eq!(format_cnpj("00111222000133"), None);
assert_eq!(format_cnpj("00000000000000"), None);
assert_eq!(format_cnpj("12345"), None);
}
#[test]
fn test_validate() {
assert!(validate("34665388000161"));
assert!(validate("03560714000142"));
assert!(validate("01838723000127"));
assert!(!validate("52599927000100"));
assert!(!validate("00000000000"));
assert!(!validate("00000000000000"));
assert!(!validate("11111111111111"));
assert!(!validate("00111222000133"));
}
#[test]
fn test_is_valid() {
assert!(!is_valid("1"));
assert!(!is_valid("1112223334445-"));
assert!(!is_valid("11111111111111"));
assert!(!is_valid("1111111111315"));
assert!(!is_valid("1111111111115"));
assert!(!is_valid("11111111121205"));
assert!(!is_valid("11111111113105"));
assert!(is_valid("34665388000161"));
assert!(is_valid("01838723000127"));
}
#[test]
fn test_generate() {
for _ in 0..1000 {
let cnpj = generate(None);
assert!(is_valid(&cnpj));
assert_eq!(cnpj.len(), 14);
}
for branch in [1, 100, 1234, 9999] {
let cnpj = generate(Some(branch));
assert!(is_valid(&cnpj));
assert_eq!(cnpj.len(), 14);
}
}
#[test]
fn test_hashdigit() {
assert_eq!(hashdigit("00000000000000", 13), 0);
assert_eq!(hashdigit("00000000000000", 14), 0);
assert_eq!(hashdigit("52513127000292", 13), 9);
assert_eq!(hashdigit("52513127000292", 14), 9);
assert_eq!(hashdigit("12345678901234", 13), 3);
}
#[test]
fn test_compute_checksum() {
assert_eq!(compute_checksum("000000000000"), "00");
assert_eq!(compute_checksum("525131270002"), "99");
assert_eq!(compute_checksum("123456789012"), "30");
}
#[test]
fn test_edge_cases() {
assert!(!is_valid(""));
assert!(!is_valid("123456789012"));
assert!(!is_valid("123456789012345"));
assert!(!is_valid("1234567890123a"));
assert!(!is_valid("00000000000000"));
assert!(!is_valid("99999999999999"));
}
#[test]
fn test_generate_with_zero_branch() {
let cnpj = generate(Some(0));
assert!(is_valid(&cnpj));
assert_eq!(&cnpj[8..12], "0001");
}
#[test]
fn test_generate_branch_modulo() {
let cnpj = generate(Some(10000));
assert!(is_valid(&cnpj));
assert_eq!(&cnpj[8..12], "0001");
}
}