tktax_transaction/
try_from_csv_contents.rs1crate::ix!();
3
4error_tree!{
5 pub enum ParseCsvError {
6 CsvError(csv::Error),
7 }
8}
9
10pub trait TryFromCsvContents {
11
12 fn try_from_csv_contents(file_contents: &[u8])
13 -> Result<Vec<Transaction>, ParseCsvError>;
14}
15
16impl TryFromCsvContents for Vec<Transaction> {
17
18 fn try_from_csv_contents(file_contents: &[u8])
19 -> Result<Vec<Transaction>, ParseCsvError>
20 {
21 let input_reader = Cursor::new(file_contents);
22
23 let mut reader = ReaderBuilder::new()
24 .trim(Trim::All)
25 .has_headers(true)
26 .flexible(true)
27 .terminator(csv::Terminator::Any(b'\n'))
28 .from_reader(input_reader);
29
30 let mut transactions = Vec::new();
31
32 for result in reader.deserialize() {
33
34 let record: Transaction = result?;
35
36 transactions.push(record);
37 }
38
39 Ok(transactions)
40 }
41}
42
43
44#[cfg(test)]
45mod test_transaction_csv_parsing {
46
47 use super::*;
48 use csv::*;
49
50 #[test]
51 fn test_v1() {
52
53 pub const CSV_DATA_V1: &'static str = "\
54Date,Transaction Type,Check/Serial #,Description,Amount
5501/12/2023,Debit,,PAYMENT GAS DEBIT,($14.93)
5601/17/2023,Deposit,0,DEPOSIT,$400";
57
58 let mut rdr = ReaderBuilder::new()
59 .trim(Trim::All)
60 .has_headers(true)
61 .flexible(true)
62 .terminator(csv::Terminator::Any(b'\n'))
63 .from_reader(CSV_DATA_V1.as_bytes());
64
65 let mut txns = vec![];
66
67 for result in rdr.deserialize::<Transaction>() {
68 match result {
69 Ok(transaction) => {
70 println!("{:?}", transaction);
71 txns.push(transaction);
72 },
73 Err(e) => eprintln!("Error: {}", e),
74 }
75 }
76
77 assert!(!txns.is_empty());
78 }
79
80 #[test]
81 fn test_v2() {
82
83 pub const CSV_DATA_V2: &'static str = "\
84Posted Date,Transaction Date,Transaction Type,Check/Serial #,Description,Amount
8501/12/2023,01/12/2023,Debit,,PAYMENT GAS DEBIT,($14.93)
8601/17/2023,01/12/2023,Deposit,0,DEPOSIT,$400";
87
88 let mut rdr = ReaderBuilder::new()
89 .trim(Trim::All)
90 .has_headers(true)
91 .flexible(true)
92 .terminator(csv::Terminator::Any(b'\n'))
93 .from_reader(CSV_DATA_V2.as_bytes());
94
95 let mut txns = vec![];
96
97 for result in rdr.deserialize::<Transaction>() {
98 match result {
99 Ok(transaction) => {
100 println!("{:?}", transaction);
101 txns.push(transaction);
102 },
103 Err(e) => eprintln!("Error: {}", e),
104 }
105 }
106
107 assert!(!txns.is_empty());
108 }
109
110 #[test]
111 fn test_v2_with_posted_balances() {
112
113 pub const CSV_DATA_V2_WITH_BALANCE: &'static str = "\
114Posted Date,Transaction Date,Transaction Type,Check/Serial #,Description,Amount,Daily Posted Balance
11501/12/2023,01/12/2023,Debit,,PAYMENT GAS DEBIT,($14.93),$18.13
11601/17/2023,01/17/2023,Deposit,0,DEPOSIT,$400,$2418.13";
117
118 let mut rdr = ReaderBuilder::new()
119 .trim(Trim::All)
120 .has_headers(true)
121 .flexible(true)
122 .terminator(csv::Terminator::Any(b'\n'))
123 .from_reader(CSV_DATA_V2_WITH_BALANCE.as_bytes());
124
125 let mut txns = vec![];
126
127 for result in rdr.deserialize::<Transaction>() {
128 match result {
129 Ok(transaction) => {
130 println!("{:?}", transaction);
131 txns.push(transaction);
132 },
133 Err(e) => eprintln!("Error: {}", e),
134 }
135 }
136
137 assert!(!txns.is_empty());
138 }
139
140 #[test]
141 fn test_v2_without_posted_balances() {
142
143 pub const CSV_DATA_V2_WITHOUT_BALANCE: &'static str = "\
144Posted Date,Transaction Date,Transaction Type,Check/Serial #,Description,Amount
14501/12/2023,01/12/2023,Debit,,PAYMENT GAS DEBIT,($14.93)
14601/17/2023,01/17/2023,Deposit,0,DEPOSIT,$400";
147
148 let mut rdr = ReaderBuilder::new()
149 .trim(Trim::All)
150 .has_headers(true)
151 .flexible(true)
152 .terminator(csv::Terminator::Any(b'\n'))
153 .from_reader(CSV_DATA_V2_WITHOUT_BALANCE.as_bytes());
154
155 let mut txns = vec![];
156
157 for result in rdr.deserialize::<Transaction>() {
158 match result {
159 Ok(transaction) => {
160 println!("{:?}", transaction);
161 txns.push(transaction);
162 },
163 Err(e) => eprintln!("Error: {}", e),
164 }
165 }
166
167 assert!(!txns.is_empty());
168 }
169}