You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.2 KiB
121 lines
3.2 KiB
use std::{
|
|
fs::File,
|
|
io::{BufWriter, Write, stderr, stdout},
|
|
};
|
|
|
|
use crate::{error::Error, xlsx::XlsxReader};
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct XlsxToCsvLines {
|
|
xlsx_reader: XlsxReader,
|
|
current_row: u32,
|
|
num_rows: u32,
|
|
separator: String,
|
|
end_of_line: String,
|
|
}
|
|
|
|
impl Iterator for XlsxToCsvLines {
|
|
type Item = Result<String, Error>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.current_row > self.num_rows {
|
|
return None;
|
|
}
|
|
|
|
let mut row = self.xlsx_reader.get_row(self.current_row);
|
|
match &self.xlsx_reader.args.replace_separator_by {
|
|
Some(replacement) => {
|
|
row = row
|
|
.iter()
|
|
.map(|v| v.replace(self.xlsx_reader.args.separator, replacement.as_str()))
|
|
.collect()
|
|
}
|
|
None => {
|
|
if row
|
|
.iter()
|
|
.any(|v| v.contains(self.xlsx_reader.args.separator))
|
|
{
|
|
return Some(Err(
|
|
"Some cells contains the separator char. Use a replacement for separator char inside cells.".into(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
self.current_row += 1;
|
|
|
|
let mut row = row.join(self.separator.as_str());
|
|
match &self.xlsx_reader.args.replace_end_of_line_by {
|
|
Some(replacement) => row = row.replace(self.end_of_line.as_str(), replacement),
|
|
None => {
|
|
if row.contains(self.end_of_line.as_str()) {
|
|
return Some(Err("Some cells contains the end of line char. Use a replacement for end of line char inside cells.".into()));
|
|
}
|
|
}
|
|
}
|
|
|
|
let row = row + self.end_of_line.as_str();
|
|
|
|
Some(Ok(row))
|
|
}
|
|
}
|
|
|
|
pub enum Output {
|
|
File(String),
|
|
Stdout,
|
|
Stderr,
|
|
}
|
|
|
|
pub trait IntoOutput {
|
|
fn into_output(self) -> Output;
|
|
}
|
|
|
|
impl IntoOutput for Output {
|
|
fn into_output(self) -> Output {
|
|
self
|
|
}
|
|
}
|
|
|
|
impl IntoOutput for String {
|
|
fn into_output(self) -> Output {
|
|
Output::File(self)
|
|
}
|
|
}
|
|
|
|
impl IntoOutput for &str {
|
|
fn into_output(self) -> Output {
|
|
Output::File(String::from(self))
|
|
}
|
|
}
|
|
|
|
impl XlsxReader {
|
|
pub fn to_csv_lines(mut self) -> Result<XlsxToCsvLines, Error> {
|
|
self.finish()?;
|
|
let num_rows = self.get_worksheet_dimensions().1;
|
|
let end_of_line = self.args.end_of_line.clone();
|
|
let separator = String::from(self.args.separator);
|
|
Ok(XlsxToCsvLines {
|
|
xlsx_reader: self,
|
|
current_row: 0,
|
|
num_rows,
|
|
separator,
|
|
end_of_line,
|
|
})
|
|
}
|
|
|
|
pub fn to_csv(self, output: impl IntoOutput) -> Result<(), Error> {
|
|
let output = output.into_output();
|
|
let mut writer: Box<dyn Write> = match output {
|
|
Output::File(filename) => Box::new(BufWriter::new(File::open(filename)?)),
|
|
Output::Stdout => Box::new(BufWriter::new(stdout().lock())),
|
|
Output::Stderr => Box::new(BufWriter::new(stderr().lock())),
|
|
};
|
|
|
|
for line in self.to_csv_lines()? {
|
|
writer.write_all(line?.as_bytes())?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|