Skip to main content

document_validator/
cnpj.rs

1use 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}