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 let out = new_path(path, "-searched").with_extension("csv");
33 let wtr = Writer::file_or_stdout(self.export, &out)?;
34
35 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 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 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}