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.
xlsxtocsv/src/xlsx_to_csv.rs

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(())
}
}