Skip to main content

mrz_parser/
exceptions.rs

1//! MRZ parser errors. `MRZError` is an enum of the concrete error cases; each
2//! variant implements `Display` and `std::error::Error`.
3
4use std::error::Error;
5use std::fmt;
6
7/// Error types produced by the MRZ parser.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum MRZError {
10    /// Invalid MRZ parser input
11    InvalidMRZInput,
12
13    /// Document number hash mismatch
14    InvalidDocumentNumber,
15
16    /// Birth date hash mismatch
17    InvalidBirthDate,
18
19    /// Expiry date hash mismatch
20    InvalidExpiryDate,
21
22    /// Optional data hash mismatch
23    InvalidOptionalData,
24
25    /// Final hash mismatch
26    InvalidMRZValue,
27
28    /// A generic/custom error with a message.
29    Custom(String),
30}
31
32impl MRZError {
33    /// Convenience constructors for each variant.
34    pub fn invalid_mrz_input() -> Self {
35        MRZError::InvalidMRZInput
36    }
37
38    pub fn invalid_document_number() -> Self {
39        MRZError::InvalidDocumentNumber
40    }
41
42    pub fn invalid_birth_date() -> Self {
43        MRZError::InvalidBirthDate
44    }
45
46    pub fn invalid_expiry_date() -> Self {
47        MRZError::InvalidExpiryDate
48    }
49
50    pub fn invalid_optional_data() -> Self {
51        MRZError::InvalidOptionalData
52    }
53
54    pub fn invalid_mrz_value() -> Self {
55        MRZError::InvalidMRZValue
56    }
57
58    /// Create a custom error with a provided message.
59    pub fn custom<M: Into<String>>(msg: M) -> Self {
60        MRZError::Custom(msg.into())
61    }
62
63    /// Human-friendly message for each error variant (without the GitHub hint).
64    fn short_message(&self) -> &str {
65        match self {
66            MRZError::InvalidMRZInput => "Invalid MRZ parser input",
67            MRZError::InvalidDocumentNumber => "Document number hash mismatch",
68            MRZError::InvalidBirthDate => "Birth date hash mismatch",
69            MRZError::InvalidExpiryDate => "Expiry date hash mismatch",
70            MRZError::InvalidOptionalData => "Optional data hash mismatch",
71            MRZError::InvalidMRZValue => "Final hash mismatch",
72            MRZError::Custom(s) => s.as_str(),
73        }
74    }
75}
76
77impl fmt::Display for MRZError {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        write!(
80            f,
81            "{}. If you think this is a mistake, please file an issue at {}/issues",
82            self.short_message(),
83            env!("CARGO_PKG_REPOSITORY")
84        )
85    }
86}
87
88impl Error for MRZError {}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn display_contains_message_and_link() {
96        let e = MRZError::invalid_mrz_input();
97        let s = format!("{}", e);
98        assert!(s.contains("Invalid MRZ parser input"));
99        assert!(s.contains(concat!(env!("CARGO_PKG_REPOSITORY"), "/issues")));
100    }
101
102    #[test]
103    fn custom_error_message() {
104        let e = MRZError::custom("something went wrong");
105        let s = format!("{}", e);
106        assert!(s.contains("something went wrong"));
107        assert!(s.contains(concat!(env!("CARGO_PKG_REPOSITORY"), "/issues")));
108    }
109
110    #[test]
111    fn variants_have_expected_short_messages() {
112        assert_eq!(
113            MRZError::InvalidDocumentNumber.short_message(),
114            "Document number hash mismatch"
115        );
116        assert_eq!(
117            MRZError::InvalidBirthDate.short_message(),
118            "Birth date hash mismatch"
119        );
120        assert_eq!(
121            MRZError::InvalidExpiryDate.short_message(),
122            "Expiry date hash mismatch"
123        );
124        assert_eq!(
125            MRZError::InvalidOptionalData.short_message(),
126            "Optional data hash mismatch"
127        );
128        assert_eq!(
129            MRZError::InvalidMRZValue.short_message(),
130            "Final hash mismatch"
131        );
132    }
133}