document_validator/
cnpj.rs1use std::sync::Arc;
2
3use super::Document;
4use crate::prelude::*;
5
6const CNPJ_LENGTH: usize = 14;
7
8#[derive(PartialEq, Eq, Hash)]
9pub struct Cnpj(Arc<str>);
10
11impl AsRef<str> for Cnpj {
12 fn as_ref(&self) -> &str {
13 self.0.as_ref()
14 }
15}
16
17impl Cnpj {
18 fn generate_digit(digits: &[u8]) -> u8 {
19 let c = digits.len();
20 let result = digits
21 .iter()
22 .enumerate()
23 .map(|(index, digit)| *digit as usize * ((c - 1 - index) % 8 + 2))
24 .sum::<usize>();
25
26 let result = 11 - (result % 11);
27
28 if result < 10 {
29 result as u8
30 } else {
31 0
32 }
33 }
34}
35
36impl Document for Cnpj {
37 fn new(document: impl Into<Arc<str>>) -> Result<Self> {
38 let document: Arc<str> = document
39 .into()
40 .chars()
41 .filter(char::is_ascii_digit)
42 .collect::<String>()
43 .into();
44
45 if Self::validate(Arc::clone(&document)) {
46 Ok(Self(document))
47 } else {
48 Err(Error::ParseError(document))
49 }
50 }
51
52 fn validate(document: impl Into<Arc<str>>) -> bool {
53 let document: Arc<str> = document.into();
54
55 let digits = document
56 .bytes()
57 .filter(u8::is_ascii_digit)
58 .map(|x| x - b'0')
59 .collect::<Vec<_>>();
60
61 if digits.len() != CNPJ_LENGTH {
62 return false;
63 }
64
65 let first_digit = Self::generate_digit(&digits[..CNPJ_LENGTH - 2]);
66
67 if first_digit != digits[CNPJ_LENGTH - 2] {
68 return false;
69 }
70
71 let second_digit = Self::generate_digit(&digits[..CNPJ_LENGTH - 1]);
72
73 second_digit == digits[CNPJ_LENGTH - 1]
74 }
75
76 fn document_name(&self) -> &'static str {
77 "cnpj"
78 }
79}