Skip to content

Commit

Permalink
feat: extend the input file formats as calamine supports, fix #3
Browse files Browse the repository at this point in the history
  • Loading branch information
zitsen committed Aug 22, 2019
1 parent 2b91b20 commit f2406f0
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 56 deletions.
1 change: 1 addition & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
format_strings = true
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
name = "xlsx2csv"
version = "0.1.4-alpha.0"
authors = ["Huo Linhe <[email protected]>"]
description = "Excel XLSX to CSV converter"
description = "Excel-like sheets to CSV converter"
license = "MIT/Apache-2.0"
homepage = "https://github.com/zitsen/xlsx2csv.rs"
repository = "https://github.com/zitsen/xlsx2csv.rs"
readme = "README.md"

[dependencies]
calamine = "0.15.4"
calamine = "0.15.5"
csv = "1"
clap = "2"
pbr = "1"
Expand Down
142 changes: 88 additions & 54 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,108 +62,142 @@ extern crate pbr;

use std::path::Path;

use calamine::{open_workbook, Xlsx};
use calamine::Reader;
use calamine::DataType;
use clap::{Arg, App};
use calamine::Reader;
use calamine::{open_workbook_auto, Sheets};
use clap::{App, Arg};
use pbr::ProgressBar;

fn main() {
let matches = App::new(crate_name!())
.about(crate_description!())
.version(crate_version!())
.author(crate_authors!())
.arg(Arg::with_name("xlsx")
.help("Excel file with XLSX format")
.takes_value(true)
.required(true))
.arg(Arg::with_name("sheet_names")
.short("S")
.long("sheet_names")
.help("List sheet names if you want to use --sheets option")
.conflicts_with_all(&["sheets"]))
.arg(Arg::with_name("sheet")
.short("s")
.long("sheet")
.value_name("NAME")
.help("Select sheets")
.takes_value(true)
.multiple(true))
.arg(Arg::with_name("replace")
.short("r")
.long("replace")
.value_name("filename")
.help("replace selected sheet names")
.takes_value(true)
.multiple(true))
.arg(Arg::with_name("directory")
.short("o")
.long("directory")
.help("Output directory")
.takes_value(true)
.default_value("."))
.arg(Arg::with_name("delimiter")
.short("d")
.long("delimiter")
.help("The field delimiter for reading CSV data.")
.default_value(",")
.takes_value(true))
.arg(
Arg::with_name("xlsx")
.help(
"Input Excel like files. Support formats:\n- Excel like: .xls .xlsx .xlsb \
.xlsm .xla .xlam\n- Opendocument spreadsheets: .ods",
)
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name("sheet_names")
.short("S")
.long("sheet_names")
.help("List sheet names if you want to use --sheets option")
.conflicts_with_all(&["sheets"]),
)
.arg(
Arg::with_name("sheet")
.short("s")
.long("sheet")
.value_name("NAME")
.help("Select sheets")
.takes_value(true)
.multiple(true),
)
.arg(
Arg::with_name("replace")
.short("r")
.long("replace")
.value_name("filename")
.help("replace selected sheet names")
.takes_value(true)
.multiple(true),
)
.arg(
Arg::with_name("output")
.short("o")
.long("output")
.help("Output directory")
.takes_value(true)
.default_value("."),
)
.arg(
Arg::with_name("delimiter")
.short("d")
.long("delimiter")
.help("The field delimiter for reading CSV data.")
.default_value(",")
.takes_value(true),
)
.get_matches();
let xlsx = matches.value_of("xlsx").unwrap();
let mut workbook: Xlsx<_> = open_workbook(xlsx).expect("open xlsx file");
let mut workbook: Sheets = open_workbook_auto(xlsx).expect("open file");
let sheets = workbook.sheet_names();

if matches.is_present("sheet_names") {
for (i, sheet) in workbook.sheet_names().iter().enumerate() {
println!("{}\t{}", i, sheet);
}
return;
}

let sheets: Vec<String>= matches.values_of("sheet")
let sheets: Vec<String> = matches
.values_of("sheet")
.map(|sheet| sheet.map(|s| s.to_string()).collect())
.unwrap_or(sheets.into_iter().map(Clone::clone).collect());
let replaces: Vec<String> = matches.values_of("replace")
let replaces: Vec<String> = matches
.values_of("replace")
.map(|sheet| sheet.map(|s| s.to_string()).collect())
.unwrap_or(sheets.clone());

assert_eq!(sheets.len(), replaces.len(), "sheets number must be equal to replaces");
assert_eq!(
sheets.len(),
replaces.len(),
"sheets number must be equal to replaces"
);

let output = matches.value_of("directory").unwrap();
let delimiter = matches.value_of("delimiter").unwrap().as_bytes().first().unwrap();
let output = matches.value_of("output").unwrap();
std::fs::create_dir_all(output).unwrap();
let delimiter = matches
.value_of("delimiter")
.unwrap()
.as_bytes()
.first()
.unwrap();

for (sheet, replace) in sheets.iter().zip(replaces.iter()) {
let path = Path::new(output).join(format!("{}.csv", replace));
println!("* prepring write to {}", path.display());
let range = workbook.worksheet_range(&sheet).expect(&format!("find sheet {}", sheet)).expect("get range");
let mut wtr = csv::WriterBuilder::new().delimiter(*delimiter).from_path(path).expect("open csv");
let range = workbook
.worksheet_range(&sheet)
.expect(&format!("find sheet {}", sheet))
.expect("get range");
let mut wtr = csv::WriterBuilder::new()
.delimiter(*delimiter)
.from_path(path)
.expect("open csv");
let size = range.get_size();
println!("** sheet range size is {:?}", size);
if size.0 == 0 || size.1 == 0 {
eprintln!("worsheet range sizes should not be 0, continue");
continue;
}
println!("** start writing", );
println!("** start writing",);
let mut pb = ProgressBar::new(if size.0 > 100 { 100 } else { size.0 as _ });
let rows = range.rows();
for (i, row) in rows.enumerate() {
if size.0 <=100 {
if size.0 <= 100 {
pb.inc();
} else if i % (size.0 / 100) == 0 {
pb.inc();
}
let cols: Vec<String> = row.iter().map(|c| {
match *c {
let cols: Vec<String> = row
.iter()
.map(|c| match *c {
DataType::Int(ref c) => format!("{}", c),
DataType::Float(ref c) => format!("{}", c),
DataType::String(ref c) => format!("{}", c),
DataType::Bool(ref c) => format!("{}", c),
_ => "".to_string(),
}
}).collect();
})
.collect();
wtr.write_record(&cols).unwrap();
}
pb.finish_print("** done, flush to write csv file");
pb.finish_print(&format!("** done with sheet `{}`", sheet));
wtr.flush().unwrap();
}
}

0 comments on commit f2406f0

Please sign in to comment.