1use crate::build::{input_file_name, Data, DataRow};
13use csv::Reader;
14use csv::StringRecord;
15use std::fs::File;
16use tera::{Map, Value};
17
18#[macro_export]
23macro_rules! insert_field {
24 ($value:expr => $row:ident, $name:expr) => {
25 $row.insert($name.to_string(), $value.into());
26 };
27 ($record:ident, $index:expr => $row:ident, $name:expr) => {
28 $row.insert($name.to_string(), $record.get($index).unwrap().into());
29 };
30 ($record:ident, $index:expr => $row:ident, $name:expr) => {
31 $row.insert($name.to_string(), $record.get($index).unwrap().into());
32 };
33 ($record:ident, $row:ident, $($index:expr => $name:expr),+) => {
34 $(
35 insert_field!($record, $index => $row, $name);
36 )+
37 };
38 ($record:ident, $index:expr => $row:ident, $name:expr, $field_type:ty) => {{
39 let temp = $record.get($index).unwrap();
40 let temp = <$field_type>::from_str(temp).unwrap();
41 $row.insert($name.to_string(), temp.into());
42 }};
43 ($record:ident, $row:ident, $($index:expr => $name:expr, $field_type:ty),+) => {
44 $(
45 insert_field!($record, $index => $row, $name, $field_type);
46 )+
47 };
48}
49
50#[macro_export]
51macro_rules! insert_optional_field {
52 ($record:ident, $index:expr => $row:ident, $name:expr) => {{
53 let temp = $record.get($index).unwrap();
54 if !temp.is_empty() {
55 $row.insert($name.to_string(), temp.into());
56 }
57 }};
58 ($record:ident, $row:ident, $($index:expr => $name:expr),+) => {
59 $(
60 insert_optional_field!($record, $index => $row, $name);
61 )+
62 };
63}
64
65pub fn process_scsv_input<D, F>(
70 data: D,
71 file_name: &str,
72 process_row: F,
73) -> Result<D, Box<dyn std::error::Error>>
74where
75 D: Data,
76 F: Fn(StringRecord, &mut DataRow) -> Result<String, Box<dyn std::error::Error>>,
77{
78 process_vsv_input(data, file_name, b';', process_row)
79}
80
81pub fn process_csv_input<D, F>(
82 data: D,
83 file_name: &str,
84 process_row: F,
85) -> Result<D, Box<dyn std::error::Error>>
86where
87 D: Data,
88 F: Fn(StringRecord, &mut DataRow) -> Result<String, Box<dyn std::error::Error>>,
89{
90 process_vsv_input(data, file_name, b',', process_row)
91}
92
93pub fn process_tsv_input<D, F>(
94 data: D,
95 file_name: &str,
96 process_row: F,
97) -> Result<D, Box<dyn std::error::Error>>
98where
99 D: Data,
100 F: Fn(StringRecord, &mut DataRow) -> Result<String, Box<dyn std::error::Error>>,
101{
102 process_vsv_input(data, file_name, b'\t', process_row)
103}
104
105pub fn open_csv_file(
106 file_name: &str,
107 delimiter: Option<u8>,
108) -> Result<Reader<File>, Box<dyn std::error::Error>> {
109 let file_name = input_file_name(file_name);
110
111 Ok(csv::ReaderBuilder::new()
112 .has_headers(true)
113 .delimiter(delimiter.unwrap_or(b','))
114 .comment(Some(b'#'))
115 .trim(csv::Trim::All)
116 .from_reader(File::open(file_name)?))
117}
118
119fn process_vsv_input<D, F>(
120 mut data: D,
121 file_name: &str,
122 delimiter: u8,
123 process_row: F,
124) -> Result<D, Box<dyn std::error::Error>>
125where
126 D: Data,
127 F: Fn(StringRecord, &mut DataRow) -> Result<String, Box<dyn std::error::Error>>,
128{
129 let mut rdr = open_csv_file(file_name, Some(delimiter))?;
130
131 for result in rdr.records() {
132 let record = result?;
133
134 let mut row: Map<String, Value> = Default::default();
135
136 let id = process_row(record, &mut row)?;
137
138 data.insert_row(&id, row);
139 }
140
141 Ok(data)
142}