rsv_lib/utils/
to.rs

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    // out path
27    let out = out_filename(out);
28
29    // rdr and wtr
30    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    // copy
37    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    // out path
53    let out = out_filename(out);
54
55    // rdr and wtr
56    let range = ExcelReader::new(path, sheet)?;
57    let mut wtr = Writer::new(Path::new(&out))?;
58
59    // excel2csv
60    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    // out path
72    let out = out_filename(out);
73
74    // rdr and wtr
75    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    // column type
80    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    // copy
89    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    // out path
102    let out = out_filename(out);
103
104    // rdr
105    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    //  wtr
120    let workbook = Workbook::new(out.to_str().unwrap())?;
121    let mut sheet = workbook.add_worksheet(None)?;
122    let ctypes = if equal_width(&lines) {
123        // column type
124        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}