tktax_transaction/
try_from_csv_contents.rs

1// ---------------- [ File: tktax-transaction/src/try_from_csv_contents.rs ]
2crate::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}