rsv_lib/excel/
search.rs

1use crate::args::Search;
2use crate::utils::cli_result::CliResult;
3use crate::utils::column::Columns;
4use crate::utils::constants::COMMA;
5use crate::utils::excel::datatype_vec_to_string_vec;
6use crate::utils::filename::new_path;
7use crate::utils::regex::Re;
8use crate::utils::util::werr_exit;
9use crate::utils::writer::Writer;
10use calamine::{open_workbook_auto, Reader, Sheets};
11use std::fs::File;
12use std::io::BufReader;
13
14struct Args<'a> {
15    sheet: usize,
16    cols_raw: &'a str,
17    cols: Columns<'a>,
18    filter_raw: &'a str,
19    filter: Columns<'a>,
20    no_header: bool,
21    wtr: Writer,
22    re: Re,
23    matched: usize,
24    workbook: Sheets<BufReader<File>>,
25}
26
27impl Search {
28    pub fn excel_run(&self) -> CliResult {
29        let path = &self.path();
30
31        // wtr and rdr
32        let out = new_path(path, "-searched").with_extension("csv");
33        let wtr = Writer::file_or_stdout(self.export, &out)?;
34
35        // regex search
36        let mut args = Args {
37            sheet: 0,
38            cols_raw: &self.cols,
39            cols: Columns::new(&self.cols),
40            filter_raw: &self.filter,
41            filter: Columns::new(&self.filter),
42            no_header: self.no_header,
43            wtr,
44            re: Re::new(&self.pattern)?,
45            matched: 0,
46            workbook: open_workbook_auto(path)?,
47        };
48
49        if self.sheet == "all" {
50            args.search_all()?
51        } else {
52            args.parse_sheet(&self.sheet);
53            args.search_one()?
54        };
55
56        if self.export {
57            println!("Matched rows: {}", args.matched);
58            println!("Saved to file: {}", out.display());
59        }
60
61        Ok(())
62    }
63}
64
65impl<'a> Args<'a> {
66    fn parse_sheet(&mut self, sheet: &str) {
67        let Ok(v) = sheet.parse::<usize>() else {
68            werr_exit!("{} is not a valid int.", sheet);
69        };
70
71        self.sheet = v;
72    }
73
74    fn search_one(&mut self) -> CliResult {
75        self.search(self.sheet as usize);
76        Ok(())
77    }
78
79    fn search_all(&mut self) -> CliResult {
80        let sheets = self.workbook.sheet_names().to_owned();
81
82        for (i, sheet) in sheets.iter().enumerate() {
83            write!(self.wtr.0, "[{}]\n", sheet)?;
84            self.search(i);
85            write!(self.wtr.0, "{}\n", "")?;
86        }
87
88        Ok(())
89    }
90
91    fn search(&mut self, sheet: usize) {
92        let Ok(range) = self.workbook.worksheet_range_at(sheet).unwrap_or_else(|| {
93            werr_exit!("{}-th sheet does not exist.", sheet);
94        }) else {
95            return;
96        };
97
98        let n = range.get_size().1;
99        self.cols = Columns::new(self.cols_raw).total_col(n).parse();
100        self.filter = Columns::new(self.filter_raw).total_col(n).parse();
101
102        let mut rows = range.rows();
103
104        // header
105        if !self.no_header {
106            let Some(r) = rows.next() else {
107                return;
108            };
109            if self.cols.select_all {
110                self.wtr.write_excel_line_unchecked(r, COMMA);
111            } else {
112                self.wtr
113                    .write_excel_selected_fields_unchecked(&r, &self.cols.cols, COMMA);
114            }
115        };
116
117        // read file
118        rows.for_each(|r| {
119            let r = datatype_vec_to_string_vec(r);
120            match (self.cols.select_all, self.filter.select_all) {
121                (true, true) => {
122                    if r.iter().any(|i| self.re.is_match(i)) {
123                        self.wtr.write_fields_unchecked(&r);
124                        self.matched += 1;
125                    }
126                }
127                (true, false) => {
128                    if self.filter.iter().any(|&i| self.re.is_match(&r[i])) {
129                        self.wtr.write_fields_unchecked(&r);
130                        self.matched += 1;
131                    }
132                }
133                (false, true) => {
134                    if r.iter().any(|i| self.re.is_match(i)) {
135                        self.wtr
136                            .write_selected_fields_unchecked(&r, &self.cols.cols, None);
137                        self.matched += 1;
138                    }
139                }
140                (false, false) => {
141                    if self.filter.iter().any(|&i| self.re.is_match(&r[i])) {
142                        self.wtr
143                            .write_selected_fields_unchecked(&r, &self.cols.cols, None);
144                        self.matched += 1;
145                    }
146                }
147            }
148        })
149    }
150}