rsv_lib/utils/
writer.rs

1use super::{cli_result::CliResult, constants::TERMINATOR};
2use calamine::Data;
3use chrono::Timelike;
4use std::{
5    fs::{File, OpenOptions},
6    io::{stdout, BufWriter, Error, Write},
7    path::Path,
8    process,
9};
10
11pub struct Writer(pub Box<dyn Write>);
12
13impl Writer {
14    pub fn new(path: &Path) -> Result<Self, Error> {
15        let wtr = Box::new(BufWriter::new(File::create(path)?));
16
17        Ok(Writer(wtr))
18    }
19
20    pub fn file_or_stdout(export: bool, path: &Path) -> Result<Self, Error> {
21        let wtr = match export {
22            true => Box::new(BufWriter::new(File::create(path)?)) as Box<dyn Write>,
23            false => Box::new(stdout()) as Box<dyn Write>,
24        };
25
26        Ok(Writer(wtr))
27    }
28
29    pub fn stdout() -> Result<Self, Error> {
30        let wtr = Box::new(stdout()) as Box<dyn Write>;
31        Ok(Writer(wtr))
32    }
33
34    pub fn append_to(out: &Path) -> Result<Self, Error> {
35        // open file
36        let f = OpenOptions::new()
37            .write(true)
38            .append(true)
39            .create(true)
40            .open(out)?;
41
42        let wtr = Box::new(BufWriter::new(f));
43
44        Ok(Writer(wtr))
45    }
46
47    pub fn write_bytes(&mut self, bytes: &[u8]) -> CliResult {
48        self.0.write_all(bytes)?;
49        Ok(())
50    }
51
52    pub fn write_bytes_unchecked(&mut self, bytes: &[u8]) {
53        if self.0.write_all(bytes).is_err() {
54            process::exit(0)
55        }
56    }
57
58    pub fn write_header(&mut self, row: &str) -> CliResult {
59        if !row.is_empty() {
60            self.write_str(row)?;
61        }
62        Ok(())
63    }
64
65    // pub fn write_header_unchecked(&mut self, row: &str) {
66    //     if self.write_header(row).is_err() {
67    //         process::exit(0)
68    //     }
69    // }
70
71    pub fn write_str<T: AsRef<str>>(&mut self, row: T) -> CliResult {
72        self.0.write_all(row.as_ref().as_bytes())?;
73        self.0.write_all(TERMINATOR)?;
74        Ok(())
75    }
76
77    pub fn write_str_unchecked<T: AsRef<str>>(&mut self, row: T) {
78        if self.write_str(row).is_err() {
79            process::exit(0)
80        }
81    }
82
83    pub fn write_strings<T: AsRef<str>>(&mut self, lines: &[T]) -> CliResult {
84        for l in lines {
85            self.write_str(l)?;
86        }
87        Ok(())
88    }
89
90    pub fn write_strings_unchecked<T: AsRef<str>>(&mut self, lines: &[T]) {
91        if self.write_strings(lines).is_err() {
92            process::exit(0)
93        }
94    }
95
96    pub fn write_fields<T: AsRef<str>>(&mut self, line: &[T]) -> CliResult {
97        let mut l = line.iter().peekable();
98        while let Some(f) = l.next() {
99            self.0.write_all(f.as_ref().as_bytes())?;
100            let sep = if l.peek().is_none() { TERMINATOR } else { b"," };
101            self.0.write_all(sep)?;
102        }
103
104        Ok(())
105    }
106
107    pub fn write_fields_unchecked<T: AsRef<str>>(&mut self, line: &[T]) {
108        if self.write_fields(line).is_err() {
109            process::exit(0)
110        }
111    }
112
113    pub fn write_selected_fields<T: AsRef<str>>(
114        &mut self,
115        line: &[T],
116        cols: &[usize],
117        sep: Option<&[u8]>,
118    ) -> CliResult {
119        let mut l = cols.iter().peekable();
120        while let Some(&i) = l.next() {
121            self.0.write_all(line[i].as_ref().as_bytes())?;
122            let sep = if l.peek().is_none() {
123                TERMINATOR
124            } else {
125                sep.unwrap_or(b",")
126            };
127            self.0.write_all(sep)?;
128        }
129
130        Ok(())
131    }
132
133    pub fn write_selected_fields_unchecked<T: AsRef<str>>(
134        &mut self,
135        line: &[T],
136        cols: &[usize],
137        sep: Option<&[u8]>,
138    ) {
139        if self.write_selected_fields(line, cols, sep).is_err() {
140            process::exit(0)
141        }
142    }
143
144    pub fn write_fields_of_lines_unchecked<T: AsRef<str>>(&mut self, lines: &Vec<Vec<T>>) {
145        for line in lines {
146            if self.write_fields(line).is_err() {
147                process::exit(0)
148            }
149        }
150    }
151
152    pub fn write_excel_field(&mut self, data: &Data) -> CliResult {
153        match data {
154            Data::DateTime(v) => {
155                if let Some(a) = v.as_datetime() {
156                    if a.hour() == 0 && a.minute() == 0 && a.second() == 0 {
157                        write!(&mut self.0, "{}", a.format("%Y-%m-%d"))?
158                    } else {
159                        write!(&mut self.0, "{}", a.format("%Y-%m-%d %H:%M:%S"))?
160                    }
161                }
162            }
163
164            Data::String(v) => {
165                // escape double-quote in Excel field by a \ char, we do not escape
166                // double-quote by appending a preceding double quote as in
167                // https://stackoverflow.com/questions/17808511/how-to-properly-escape-a-double-quote-in-csv
168                // this is to avoid conflict with the start and end double-quotes
169                // for a double-quoted comma-shown field.
170                let double_quote_escape_field = if v.contains("\\\"") {
171                    v
172                } else if v.contains("\"") {
173                    &v.replace("\"", "\\\"")
174                } else {
175                    v
176                };
177
178                if v.contains(',') {
179                    write!(&mut self.0, "\"{}\"", double_quote_escape_field)?
180                } else {
181                    write!(&mut self.0, "{}", double_quote_escape_field)?
182                }
183            }
184
185            _ => write!(&mut self.0, "{}", data)?,
186        }
187
188        Ok(())
189    }
190
191    pub fn write_excel_line(&mut self, line: &[Data], sep: &[u8]) -> CliResult {
192        let mut l = line.iter().peekable();
193        while let Some(f) = l.next() {
194            self.write_excel_field(f)?;
195
196            if l.peek().is_some() {
197                self.0.write_all(sep)?;
198            } else {
199                self.0.write_all(TERMINATOR)?;
200            }
201        }
202
203        Ok(())
204    }
205
206    pub fn write_excel_line_unchecked(&mut self, line: &[Data], sep: &[u8]) {
207        if self.write_excel_line(line, sep).is_err() {
208            process::exit(0)
209        }
210    }
211
212    pub fn write_excel_selected_fields(
213        &mut self,
214        line: &[Data],
215        cols: &[usize],
216        sep: &[u8],
217    ) -> CliResult {
218        let mut l = cols.iter().peekable();
219        while let Some(&i) = l.next() {
220            self.write_excel_field(&line[i])?;
221
222            if l.peek().is_some() {
223                self.0.write_all(sep)?;
224            } else {
225                self.0.write_all(TERMINATOR)?;
226            }
227        }
228
229        Ok(())
230    }
231
232    pub fn write_excel_selected_fields_unchecked(
233        &mut self,
234        line: &[Data],
235        cols: &[usize],
236        sep: &[u8],
237    ) {
238        if self.write_excel_selected_fields(line, cols, sep).is_err() {
239            process::exit(0)
240        }
241    }
242
243    pub fn write_excel_lines(&mut self, lines: &[Vec<Data>], sep: &[u8]) -> CliResult {
244        for l in lines {
245            self.write_excel_line(l, sep)?;
246        }
247
248        Ok(())
249    }
250
251    pub fn write_excel_lines_by_ref(&mut self, lines: &[&Vec<Data>], sep: &[u8]) -> CliResult {
252        for &l in lines {
253            self.write_excel_line(l, sep)?;
254        }
255
256        Ok(())
257    }
258}