Skip to main content

iso8583_core/
error.rs

1//! Error types for ISO 8583 message processing
2
3use thiserror::Error;
4
5/// Result type for ISO 8583 operations
6pub type Result<T> = std::result::Result<T, ISO8583Error>;
7
8/// Errors that can occur during ISO 8583 message processing
9#[allow(missing_docs)]
10#[derive(Error, Debug, Clone, PartialEq)]
11pub enum ISO8583Error {
12    /// Invalid message type indicator
13    #[error("Invalid MTI: {0}")]
14    InvalidMTI(String),
15
16    /// Invalid field number
17    #[error("Invalid field number: {0}")]
18    InvalidFieldNumber(u8),
19
20    /// Field not present in message
21    #[error("Field {0} not present in message")]
22    FieldNotPresent(u8),
23
24    /// Invalid field value
25    #[error("Invalid value for field {field}: {reason}")]
26    InvalidFieldValue { field: u8, reason: String },
27
28    /// Field length mismatch
29    #[error("Field {field} length mismatch: expected {expected}, got {actual}")]
30    FieldLengthMismatch {
31        field: u8,
32        expected: usize,
33        actual: usize,
34    },
35
36    /// Invalid bitmap
37    #[error("Invalid bitmap: {0}")]
38    InvalidBitmap(String),
39
40    /// Invalid encoding
41    #[error("Invalid encoding: {0}")]
42    InvalidEncoding(String),
43
44    /// Message too short
45    #[error("Message too short: expected at least {expected} bytes, got {actual}")]
46    MessageTooShort { expected: usize, actual: usize },
47
48    /// Invalid PAN (Primary Account Number)
49    #[error("Invalid PAN: {0}")]
50    InvalidPAN(String),
51
52    /// Luhn check failed
53    #[error("Luhn check failed for PAN")]
54    LuhnCheckFailed,
55
56    /// Invalid amount
57    #[error("Invalid amount: {0}")]
58    InvalidAmount(String),
59
60    /// Invalid date/time
61    #[error("Invalid date/time in field {field}: {reason}")]
62    InvalidDateTime { field: u8, reason: String },
63
64    /// Missing required field
65    #[error("Missing required field: {0}")]
66    MissingRequiredField(u8),
67
68    /// Parse error
69    #[error("Parse error: {0}")]
70    ParseError(String),
71
72    /// Encoding error
73    #[error("Encoding error: {0}")]
74    EncodingError(String),
75
76    /// Validation error
77    #[error("Validation error: {0}")]
78    ValidationError(String),
79
80    /// Builder error
81    #[error("Builder error: {0}")]
82    BuilderError(String),
83
84    /// Invalid message class
85    #[error("Invalid message class: {0}")]
86    InvalidMessageClass(String),
87
88    /// Invalid message function
89    #[error("Invalid message function: {0}")]
90    InvalidMessageFunction(String),
91
92    /// Invalid message origin
93    #[error("Invalid message origin: {0}")]
94    InvalidMessageOrigin(String),
95
96    /// Custom error
97    #[error("Custom error: {0}")]
98    Custom(String),
99}
100
101impl ISO8583Error {
102    /// Create a custom error
103    pub fn custom<S: Into<String>>(msg: S) -> Self {
104        ISO8583Error::Custom(msg.into())
105    }
106
107    /// Create an invalid field value error
108    pub fn invalid_field_value<S: Into<String>>(field: u8, reason: S) -> Self {
109        ISO8583Error::InvalidFieldValue {
110            field,
111            reason: reason.into(),
112        }
113    }
114
115    /// Create a field length mismatch error
116    pub fn field_length_mismatch(field: u8, expected: usize, actual: usize) -> Self {
117        ISO8583Error::FieldLengthMismatch {
118            field,
119            expected,
120            actual,
121        }
122    }
123
124    /// Create a message too short error
125    pub fn message_too_short(expected: usize, actual: usize) -> Self {
126        ISO8583Error::MessageTooShort { expected, actual }
127    }
128
129    /// Create an invalid date/time error
130    pub fn invalid_datetime<S: Into<String>>(field: u8, reason: S) -> Self {
131        ISO8583Error::InvalidDateTime {
132            field,
133            reason: reason.into(),
134        }
135    }
136}
137
138// Conversion from &'static str to ISO8583Error for easy error handling
139impl From<&'static str> for ISO8583Error {
140    fn from(s: &'static str) -> Self {
141        ISO8583Error::Custom(s.to_string())
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn test_error_display() {
151        let err = ISO8583Error::InvalidMTI("9999".to_string());
152        assert_eq!(err.to_string(), "Invalid MTI: 9999");
153
154        let err = ISO8583Error::FieldNotPresent(2);
155        assert_eq!(err.to_string(), "Field 2 not present in message");
156
157        let err = ISO8583Error::invalid_field_value(4, "Amount cannot be negative");
158        assert_eq!(
159            err.to_string(),
160            "Invalid value for field 4: Amount cannot be negative"
161        );
162    }
163
164    #[test]
165    fn test_error_equality() {
166        let err1 = ISO8583Error::InvalidMTI("0100".to_string());
167        let err2 = ISO8583Error::InvalidMTI("0100".to_string());
168        let err3 = ISO8583Error::InvalidMTI("0200".to_string());
169
170        assert_eq!(err1, err2);
171        assert_ne!(err1, err3);
172    }
173}