1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, ISO8583Error>;
7
8#[allow(missing_docs)]
10#[derive(Error, Debug, Clone, PartialEq)]
11pub enum ISO8583Error {
12 #[error("Invalid MTI: {0}")]
14 InvalidMTI(String),
15
16 #[error("Invalid field number: {0}")]
18 InvalidFieldNumber(u8),
19
20 #[error("Field {0} not present in message")]
22 FieldNotPresent(u8),
23
24 #[error("Invalid value for field {field}: {reason}")]
26 InvalidFieldValue { field: u8, reason: String },
27
28 #[error("Field {field} length mismatch: expected {expected}, got {actual}")]
30 FieldLengthMismatch {
31 field: u8,
32 expected: usize,
33 actual: usize,
34 },
35
36 #[error("Invalid bitmap: {0}")]
38 InvalidBitmap(String),
39
40 #[error("Invalid encoding: {0}")]
42 InvalidEncoding(String),
43
44 #[error("Message too short: expected at least {expected} bytes, got {actual}")]
46 MessageTooShort { expected: usize, actual: usize },
47
48 #[error("Invalid PAN: {0}")]
50 InvalidPAN(String),
51
52 #[error("Luhn check failed for PAN")]
54 LuhnCheckFailed,
55
56 #[error("Invalid amount: {0}")]
58 InvalidAmount(String),
59
60 #[error("Invalid date/time in field {field}: {reason}")]
62 InvalidDateTime { field: u8, reason: String },
63
64 #[error("Missing required field: {0}")]
66 MissingRequiredField(u8),
67
68 #[error("Parse error: {0}")]
70 ParseError(String),
71
72 #[error("Encoding error: {0}")]
74 EncodingError(String),
75
76 #[error("Validation error: {0}")]
78 ValidationError(String),
79
80 #[error("Builder error: {0}")]
82 BuilderError(String),
83
84 #[error("Invalid message class: {0}")]
86 InvalidMessageClass(String),
87
88 #[error("Invalid message function: {0}")]
90 InvalidMessageFunction(String),
91
92 #[error("Invalid message origin: {0}")]
94 InvalidMessageOrigin(String),
95
96 #[error("Custom error: {0}")]
98 Custom(String),
99}
100
101impl ISO8583Error {
102 pub fn custom<S: Into<String>>(msg: S) -> Self {
104 ISO8583Error::Custom(msg.into())
105 }
106
107 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 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 pub fn message_too_short(expected: usize, actual: usize) -> Self {
126 ISO8583Error::MessageTooShort { expected, actual }
127 }
128
129 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
138impl 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}