1use std::fmt;
10
11#[allow(missing_docs)]
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct ProcessingCode {
15 pub transaction_type: TransactionType,
16 pub from_account: AccountType,
17 pub to_account: AccountType,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub enum TransactionType {
23 Purchase = 0,
25 CashWithdrawal = 1,
27 DebitAdjustment = 2,
29 CheckGuarantee = 3,
31 CheckVerification = 4,
33 Eurocheque = 5,
35 TravelersCheck = 6,
37 LetterOfCredit = 7,
39 Giro = 8,
41 CashDeposit = 21,
43 CheckDeposit = 22,
45 BalanceInquiry = 31,
47 MiniStatement = 38,
49 TransferCheckingToSavings = 40,
51 TransferSavingsToChecking = 41,
53 Refund = 20,
55 Payment = 50,
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
61pub enum AccountType {
62 Default = 0,
64 Savings = 10,
66 Checking = 20,
68 Credit = 30,
70 Universal = 40,
72 Investment = 50,
74}
75
76impl ProcessingCode {
77 pub const PURCHASE: Self = Self {
81 transaction_type: TransactionType::Purchase,
82 from_account: AccountType::Default,
83 to_account: AccountType::Default,
84 };
85
86 pub const WITHDRAWAL_CHECKING: Self = Self {
88 transaction_type: TransactionType::CashWithdrawal,
89 from_account: AccountType::Default,
90 to_account: AccountType::Default,
91 };
92
93 pub const WITHDRAWAL_SAVINGS: Self = Self {
95 transaction_type: TransactionType::CashWithdrawal,
96 from_account: AccountType::Savings,
97 to_account: AccountType::Default,
98 };
99
100 pub const DEPOSIT_CHECKING: Self = Self {
102 transaction_type: TransactionType::CashDeposit,
103 from_account: AccountType::Default,
104 to_account: AccountType::Default,
105 };
106
107 pub const DEPOSIT_SAVINGS: Self = Self {
109 transaction_type: TransactionType::CashDeposit,
110 from_account: AccountType::Savings,
111 to_account: AccountType::Default,
112 };
113
114 pub const BALANCE_INQUIRY_CHECKING: Self = Self {
116 transaction_type: TransactionType::BalanceInquiry,
117 from_account: AccountType::Default,
118 to_account: AccountType::Default,
119 };
120
121 pub const BALANCE_INQUIRY_SAVINGS: Self = Self {
123 transaction_type: TransactionType::BalanceInquiry,
124 from_account: AccountType::Savings,
125 to_account: AccountType::Default,
126 };
127
128 pub const REFUND: Self = Self {
130 transaction_type: TransactionType::Refund,
131 from_account: AccountType::Default,
132 to_account: AccountType::Default,
133 };
134
135 pub const TRANSFER_CHECKING_TO_SAVINGS: Self = Self {
137 transaction_type: TransactionType::TransferCheckingToSavings,
138 from_account: AccountType::Savings,
139 to_account: AccountType::Checking,
140 };
141
142 pub fn new(
144 transaction_type: TransactionType,
145 from_account: AccountType,
146 to_account: AccountType,
147 ) -> Self {
148 Self {
149 transaction_type,
150 from_account,
151 to_account,
152 }
153 }
154
155 pub fn description(&self) -> String {
157 let txn_desc = match self.transaction_type {
158 TransactionType::Purchase => "Purchase",
159 TransactionType::CashWithdrawal => "Cash Withdrawal",
160 TransactionType::CashDeposit => "Deposit",
161 TransactionType::BalanceInquiry => "Balance Inquiry",
162 TransactionType::Refund => "Refund",
163 TransactionType::Payment => "Payment",
164 TransactionType::TransferCheckingToSavings => "Transfer",
165 TransactionType::TransferSavingsToChecking => "Transfer",
166 _ => "Transaction",
167 };
168
169 let from_desc = match self.from_account {
170 AccountType::Savings => " from Savings",
171 AccountType::Checking => " from Checking",
172 AccountType::Credit => " from Credit",
173 AccountType::Default => "",
174 _ => " from Account",
175 };
176
177 let to_desc = match self.to_account {
178 AccountType::Savings => " to Savings",
179 AccountType::Checking => " to Checking",
180 AccountType::Credit => " to Credit",
181 AccountType::Default => "",
182 _ => " to Account",
183 };
184
185 format!("{}{}{}", txn_desc, from_desc, to_desc)
186 }
187
188 pub fn is_inquiry(&self) -> bool {
190 matches!(
191 self.transaction_type,
192 TransactionType::BalanceInquiry | TransactionType::MiniStatement
193 )
194 }
195
196 pub fn is_cash(&self) -> bool {
198 matches!(
199 self.transaction_type,
200 TransactionType::CashWithdrawal | TransactionType::CashDeposit
201 )
202 }
203
204 pub fn is_transfer(&self) -> bool {
206 matches!(
207 self.transaction_type,
208 TransactionType::TransferCheckingToSavings | TransactionType::TransferSavingsToChecking
209 )
210 }
211}
212
213impl std::str::FromStr for ProcessingCode {
214 type Err = ();
215
216 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
217 if s.len() != 6 {
218 return Err(());
219 }
220
221 let tt = s[0..2].parse::<u8>().map_err(|_| ())?;
222 let from = s[2..4].parse::<u8>().map_err(|_| ())?;
223 let to = s[4..6].parse::<u8>().map_err(|_| ())?;
224
225 Ok(Self {
226 transaction_type: TransactionType::from_code(tt).ok_or(())?,
227 from_account: AccountType::from_code(from).ok_or(())?,
228 to_account: AccountType::from_code(to).ok_or(())?,
229 })
230 }
231}
232
233#[allow(missing_docs)]
234impl TransactionType {
235 pub fn from_code(code: u8) -> Option<Self> {
236 match code {
237 0 => Some(Self::Purchase),
238 1 => Some(Self::CashWithdrawal),
239 2 => Some(Self::DebitAdjustment),
240 20 => Some(Self::Refund),
241 21 => Some(Self::CashDeposit),
242 22 => Some(Self::CheckDeposit),
243 31 => Some(Self::BalanceInquiry),
244 38 => Some(Self::MiniStatement),
245 40 => Some(Self::TransferCheckingToSavings),
246 41 => Some(Self::TransferSavingsToChecking),
247 50 => Some(Self::Payment),
248 _ => None,
249 }
250 }
251
252 pub fn to_code(&self) -> u8 {
253 match self {
254 Self::Purchase => 0,
255 Self::CashWithdrawal => 1,
256 Self::DebitAdjustment => 2,
257 Self::Refund => 20,
258 Self::CashDeposit => 21,
259 Self::CheckDeposit => 22,
260 Self::BalanceInquiry => 31,
261 Self::MiniStatement => 38,
262 Self::TransferCheckingToSavings => 40,
263 Self::TransferSavingsToChecking => 41,
264 Self::Payment => 50,
265 _ => 0,
266 }
267 }
268}
269
270#[allow(missing_docs)]
271impl AccountType {
272 pub fn from_code(code: u8) -> Option<Self> {
273 match code {
274 0 => Some(Self::Default),
275 10 => Some(Self::Savings),
276 20 => Some(Self::Checking),
277 30 => Some(Self::Credit),
278 40 => Some(Self::Universal),
279 50 => Some(Self::Investment),
280 _ => Some(Self::Default), }
282 }
283
284 pub fn to_code(&self) -> u8 {
285 *self as u8
286 }
287}
288
289impl fmt::Display for ProcessingCode {
290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291 write!(
292 f,
293 "{:02}{:02}{:02}",
294 self.transaction_type.to_code(),
295 self.from_account.to_code(),
296 self.to_account.to_code()
297 )
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304
305 #[test]
306 fn test_processing_codes() {
307 assert_eq!(ProcessingCode::PURCHASE.to_string(), "000000");
308 assert_eq!(ProcessingCode::WITHDRAWAL_CHECKING.to_string(), "010000");
309 assert_eq!(
310 ProcessingCode::BALANCE_INQUIRY_SAVINGS.to_string(),
311 "311000"
312 );
313 }
314
315 #[test]
316 fn test_from_string() {
317 let code = "000000".parse::<ProcessingCode>().unwrap();
318 assert_eq!(code, ProcessingCode::PURCHASE);
319
320 let code = "010000".parse::<ProcessingCode>().unwrap();
321 assert_eq!(code, ProcessingCode::WITHDRAWAL_CHECKING);
322 }
323
324 #[test]
325 fn test_descriptions() {
326 assert_eq!(ProcessingCode::PURCHASE.description(), "Purchase");
327 assert_eq!(
328 ProcessingCode::WITHDRAWAL_SAVINGS.description(),
329 "Cash Withdrawal from Savings"
330 );
331 assert_eq!(
332 ProcessingCode::BALANCE_INQUIRY_CHECKING.description(),
333 "Balance Inquiry"
334 );
335 }
336
337 #[test]
338 fn test_predicates() {
339 assert!(ProcessingCode::BALANCE_INQUIRY_CHECKING.is_inquiry());
340 assert!(ProcessingCode::WITHDRAWAL_CHECKING.is_cash());
341 assert!(!ProcessingCode::PURCHASE.is_cash());
342 }
343}