codes_common/build/
csv.rs

1/*!
2One-line description.
3
4More detailed description, with
5
6# Example
7
8YYYYY
9
10*/
11
12use crate::build::{input_file_name, Data, DataRow};
13use csv::Reader;
14use csv::StringRecord;
15use std::fs::File;
16use tera::{Map, Value};
17
18// ------------------------------------------------------------------------------------------------
19// Public Macros
20// ------------------------------------------------------------------------------------------------
21
22#[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
65// ------------------------------------------------------------------------------------------------
66// Public Functions
67// ------------------------------------------------------------------------------------------------
68
69pub 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}