Skip to main content

ftracker_identifiers/cfi/
error.rs

1use core::fmt;
2
3/// The set of reasons a CFI string can fail validation.
4///
5/// Every fallible constructor of [`Cfi`](super::Cfi) returns this type. Unlike CNPJ or ISIN, a CFI
6/// carries no checksum; instead its validity is defined by the ISO 10962 code taxonomy, so beyond
7/// the structural checks there are three *taxonomic* failure modes ([`CfiError::UnknownCategory`],
8/// [`CfiError::UnknownGroup`], [`CfiError::InvalidAttribute`]). Each variant maps to a single,
9/// specific failure so callers can react programmatically rather than parsing a message.
10#[non_exhaustive]
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum CfiError {
13    /// The input was an empty string.
14    Empty,
15
16    /// After trimming surrounding whitespace, the input did not contain exactly 6 characters.
17    InvalidLength {
18        /// The number of characters found after trimming.
19        found: usize,
20    },
21
22    /// A character outside `A`–`Z` was found at a given position. Every CFI position is an
23    /// uppercase ASCII letter.
24    InvalidCharacter {
25        /// The offending character, as originally provided (before case folding).
26        character: char,
27        /// 1-indexed position within the 6 characters.
28        position: u8,
29    },
30
31    /// The category letter (position 1) is not one defined by ISO 10962.
32    UnknownCategory {
33        /// The offending category code.
34        code: char,
35    },
36
37    /// The group letter (position 2) is not defined for the otherwise-valid category.
38    UnknownGroup {
39        /// The (valid) category code the group was looked up under.
40        category: char,
41        /// The offending group code.
42        code: char,
43    },
44
45    /// An attribute letter (positions 3–6) is not permitted for the resolved category and group at
46    /// that attribute position.
47    InvalidAttribute {
48        /// The (valid) category code.
49        category: char,
50        /// The (valid) group code.
51        group: char,
52        /// Which attribute failed, 1–4 (corresponding to CFI positions 3–6).
53        index: u8,
54        /// The offending attribute code.
55        code: char,
56    },
57}
58
59impl fmt::Display for CfiError {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        match self {
62            CfiError::Empty => f.write_str("CFI input is empty"),
63            CfiError::InvalidLength { found } => {
64                write!(f, "CFI must contain exactly 6 characters, found {found}")
65            }
66            CfiError::InvalidCharacter {
67                character,
68                position,
69            } => write!(
70                f,
71                "invalid character '{character}' at position {position} of 6: expected an uppercase letter (A-Z)"
72            ),
73            CfiError::UnknownCategory { code } => {
74                write!(f, "unknown CFI category '{code}' at position 1")
75            }
76            CfiError::UnknownGroup { category, code } => write!(
77                f,
78                "unknown CFI group '{code}' at position 2 for category '{category}'"
79            ),
80            CfiError::InvalidAttribute {
81                category,
82                group,
83                index,
84                code,
85            } => write!(
86                f,
87                "invalid CFI attribute '{code}' at position {} (attribute {index}) for category '{category}' group '{group}'",
88                index + 2
89            ),
90        }
91    }
92}
93
94impl core::error::Error for CfiError {}