ftracker_identifiers/cnpj/error.rs
1use core::fmt;
2
3/// The set of characters permitted at a given position of a CNPJ.
4///
5/// Reported by [`CnpjError::InvalidCharacter`] to describe what was expected where an invalid
6/// character was found.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum CharacterClass {
9 /// An ASCII digit, `'0'...='9'`.
10 Digit,
11 /// An ASCII digit or an uppercase ASCII letter, `'0'...='9' | 'A'...='Z'`.
12 AlphanumericUppercase,
13}
14
15impl fmt::Display for CharacterClass {
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 match self {
18 CharacterClass::Digit => write!(f, "a digit (0-9)"),
19 CharacterClass::AlphanumericUppercase => {
20 write!(f, "a digit (0-9) or an uppercase letter (A-Z)")
21 }
22 }
23 }
24}
25
26/// The error returned when a [`Cnpj`](crate::Cnpj) fails to parse or validate.
27///
28/// Each variant corresponds to one validation rule, in the order the rules are applied. See the
29/// [module level documentation](crate::cnpj) for the full rule list.
30#[non_exhaustive]
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum CnpjError {
33 /// The input was an empty string.
34 Empty,
35
36 /// After stripping punctuation (`.`, `/`, `-`, whitespace), the input did not contain exactly
37 /// 14 meaningful characters.
38 InvalidLength {
39 /// The number of meaningful (non-punctuation) characters found.
40 found: usize,
41 },
42
43 /// A character outside the allowed set was found at a given position.
44 InvalidCharacter {
45 /// The offending character, as originally provided (before case folding).
46 character: char,
47 /// 1-indexed position within the 14 meaningful characters.
48 position: u8,
49 /// The character class that was expected at this position.
50 expected: CharacterClass,
51 },
52
53 /// The Módulo 11 checksum did not match one of the two verification digits.
54 InvalidCheckDigits {
55 /// 1-indexed position of the mismatching verification digit (13 or 14).
56 position: u8,
57 /// The verification digit computed from the Módulo 11 algorithm.
58 expected: u8,
59 /// The verification digit actually present in the input.
60 found: u8,
61 },
62
63 /// All 14 characters were identical (e.g. `"00000000000000"`).
64 ///
65 /// Such inputs are structurally well-formed and can even satisfy the Módulo 11 checksum for
66 /// certain repeated digits, but the Receita Federal never issues them;
67 /// they are reliably placeholder or data-entry artifacts.
68 RepeatedDigits,
69}
70
71impl fmt::Display for CnpjError {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 match self {
74 CnpjError::Empty => f.write_str("CNPJ input is empty"),
75 CnpjError::InvalidLength { found } => write!(
76 f,
77 "CNPJ must contain exactly 14 characters once formatting is removed, found {found}"
78 ),
79 CnpjError::InvalidCharacter {
80 character,
81 position,
82 expected,
83 } => write!(
84 f,
85 "invalid character '{character}' at position {position} of 14: expected {expected}"
86 ),
87 CnpjError::InvalidCheckDigits {
88 position,
89 expected,
90 found,
91 } => write!(
92 f,
93 "invalid check digit at position {position} of 14: expected {expected}, found {found}"
94 ),
95 CnpjError::RepeatedDigits => {
96 f.write_str("CNPJ cannot consist of a single character repeated 14 times")
97 }
98 }
99 }
100}
101
102impl core::error::Error for CnpjError {}