1use super::{cli_result::CliResult, filename::new_file, reader::ExcelReader, writer::Writer};
2use crate::{
3 args::To,
4 utils::{column::Columns, column_type::ColumnTypes, row_split::CsvRowSplitter},
5};
6use std::{
7 fs::File,
8 io::{stdin, BufRead, BufReader, BufWriter, Write},
9 path::{Path, PathBuf},
10};
11use xlsxwriter::{Workbook, Worksheet};
12
13pub fn is_file_suffix(f: &str) -> bool {
14 f == "csv" || f == "txt" || f == "tsv" || f == "xlsx" || f == "xls"
15}
16
17pub fn is_valid_plain_text(f: &str) -> bool {
18 f.ends_with("csv") || f.ends_with("txt") || f.ends_with("tsv")
19}
20
21pub fn is_valid_excel(f: &str) -> bool {
22 f.ends_with("xlsx") || f.ends_with("xls")
23}
24
25pub fn csv_or_io_to_csv(path: Option<&Path>, out: &str) -> CliResult {
26 let out = out_filename(out);
28
29 let mut rdr = match path {
31 Some(f) => Box::new(BufReader::new(File::open(f)?)) as Box<dyn BufRead>,
32 None => Box::new(BufReader::new(stdin())) as Box<dyn BufRead>,
33 };
34 let mut wtr = BufWriter::new(File::create(&out)?);
35
36 let mut buf = vec![];
38 while let Ok(bytes) = rdr.read_until(b'\n', &mut buf) {
39 if bytes == 0 {
40 break;
41 }
42 wtr.write_all(&buf[..bytes])?;
43 buf.clear();
44 }
45
46 println!("Saved to file: {}", out.display());
47
48 Ok(())
49}
50
51pub fn excel_to_csv(path: &Path, sheet: usize, sep: &str, out: &str) -> CliResult {
52 let out = out_filename(out);
54
55 let range = ExcelReader::new(path, sheet)?;
57 let mut wtr = Writer::new(Path::new(&out))?;
58
59 let sep_bytes = sep.as_bytes();
61 for r in range.iter() {
62 wtr.write_excel_line(r, sep_bytes)?;
63 }
64
65 println!("Saved to file: {}", out.display());
66
67 Ok(())
68}
69
70pub fn csv_to_excel(path: &Path, sep: char, quote: char, out: &str, no_header: bool) -> CliResult {
71 let out = out_filename(out);
73
74 let rdr = BufReader::new(File::open(path)?);
76 let workbook = Workbook::new(out.to_str().unwrap())?;
77 let mut sheet = workbook.add_worksheet(None)?;
78
79 let cols = Columns::new("").total_col_of(path, sep, quote).parse();
81 let ctypes = match ColumnTypes::guess_from_csv(path, sep, quote, no_header, &cols)? {
82 Some(v) => v,
83 None => return Ok(()),
84 };
85 ctypes.update_excel_column_width(&mut sheet)?;
86 let ctypes = Some(ctypes);
87
88 for (n, r) in rdr.lines().enumerate() {
90 let r = r?;
91 let l = CsvRowSplitter::new(&r, sep, quote).collect::<Vec<_>>();
92 write_excel_line(&mut sheet, n, &l, ctypes.as_ref())?;
93 }
94
95 println!("Saved to file: {}", out.display());
96
97 Ok(())
98}
99
100pub fn io_to_excel(args: &To, out: &str) -> CliResult {
101 let out = out_filename(out);
103
104 let lines = stdin()
106 .lock()
107 .lines()
108 .filter_map(|i| i.ok())
109 .collect::<Vec<_>>();
110 let lines = lines
111 .iter()
112 .map(|i| args.split_row_to_vec(i))
113 .collect::<Vec<_>>();
114
115 if lines.is_empty() {
116 return Ok(());
117 }
118
119 let workbook = Workbook::new(out.to_str().unwrap())?;
121 let mut sheet = workbook.add_worksheet(None)?;
122 let ctypes = if equal_width(&lines) {
123 let cols = Columns::new("").total_col(lines[0].len()).parse();
125 let ctypes = ColumnTypes::guess_from_io(&lines[(1 - args.no_header as usize)..], &cols);
126 ctypes.update_excel_column_width(&mut sheet)?;
127 Some(ctypes)
128 } else {
129 None
130 };
131
132 for (n, r) in lines.iter().enumerate() {
133 write_excel_line(&mut sheet, n, r, ctypes.as_ref())?;
134 }
135
136 println!("Saved to file: {}", out.display());
137
138 Ok(())
139}
140
141fn equal_width(lines: &Vec<Vec<&str>>) -> bool {
142 let width = lines[0].len();
143
144 for row in lines.iter() {
145 if width != row.len() {
146 return false;
147 }
148 }
149
150 true
151}
152
153fn out_filename(out: &str) -> PathBuf {
154 let f = if is_file_suffix(out) {
155 format!("export.{out}")
156 } else {
157 out.to_owned()
158 };
159
160 new_file(&f)
161}
162
163fn write_excel_line(
164 sheet: &mut Worksheet,
165 row: usize,
166 line: &[&str],
167 ctypes: Option<&ColumnTypes>,
168) -> CliResult {
169 if ctypes.is_some() {
170 for ((col, &v), t) in line.iter().enumerate().zip(ctypes.unwrap().iter()) {
171 match t.col_type.is_number() {
172 true => match v.parse::<f64>() {
173 Ok(v) => sheet.write_number(row as u32, col as u16, v, None)?,
174 Err(_) => sheet.write_string(row as u32, col as u16, v, None)?,
175 },
176 false => sheet.write_string(row as u32, col as u16, v, None)?,
177 }
178 }
179 } else {
180 for (col, &v) in line.iter().enumerate() {
181 sheet.write_string(row as u32, col as u16, v, None)?;
182 }
183 }
184
185 Ok(())
186}