Skip to main content

ftracker_identifiers/cnpj/
arbitrary.rs

1use arbitrary::{Arbitrary, Unstructured};
2
3use super::Cnpj;
4use super::validation::{BASE_LEN, avoid_all_repeated, compute_valid_check_digits};
5
6const ALPHABET: &[u8; 36] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
7
8impl<'a> Arbitrary<'a> for Cnpj {
9    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
10        let mut base = [0u8; BASE_LEN];
11        for slot in base.iter_mut() {
12            let idx = u32::from(u.arbitrary::<u8>()?) as usize % ALPHABET.len();
13            *slot = ALPHABET[idx];
14        }
15
16        // Retry on the all-repeated-character candidate, which `from_bytes` would otherwise reject.
17        avoid_all_repeated(&mut base);
18
19        let (dv1, dv2) = compute_valid_check_digits(&base);
20        let mut bytes = [0u8; 14];
21        bytes[..BASE_LEN].copy_from_slice(&base);
22        bytes[BASE_LEN] = dv1 + b'0';
23        bytes[BASE_LEN + 1] = dv2 + b'0';
24
25        Cnpj::from_bytes(bytes).map_err(|_| arbitrary::Error::IncorrectFormat)
26    }
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    #[test]
34    fn always_produces_valid_cnpjs() {
35        for seed in 0u32..256 {
36            let data = seed.to_le_bytes().repeat(8);
37            let mut u = Unstructured::new(&data);
38            let cnpj = Cnpj::arbitrary(&mut u).expect("arbitrary should always succeed");
39            // Re-validating via parse() proves the value round-trips
40            // through the exact same checks a hand-typed input would.
41            assert!(Cnpj::parse(cnpj.as_str()).is_ok());
42        }
43    }
44}