extern crate regex;
#[allow(unused_imports)]
#[macro_use]
extern crate structopt;
use structopt::StructOpt;
#[macro_use]
extern crate lazy_static;
mod errors;
mod iter;
mod patterns;
#[cfg(test)]
mod tests;
use self::{
errors::{Error, Result},
iter::Chunks,
patterns::Pattern,
};
use std::ffi::OsString;
fn main() {
match Opt::from_args().and_then(csplit) {
Ok(()) => std::process::exit(0),
Err(err) => {
eprintln!("{}", err);
std::process::exit(1);
},
}
}
pub fn csplit(opts: Opt) -> Result<()> {
let Opt {
text,
formatter,
patterns,
cleanup_after_error,
write_file_sizes_to_stdout,
} = opts;
let mut written = Vec::new();
for (i, chunk) in Chunks::new(&text, patterns).unwrap().enumerate() {
let fp = formatter.path(i);
if write_file_sizes_to_stdout {
println!("{}: {:05} bytes", &fp, chunk.len())
}
match std::fs::write(&fp, chunk) {
Err(err) => {
if cleanup_after_error {
for fp in written {
let _ = std::fs::remove_file(fp);
}
}
return Err(Error::from(err));
},
Ok(()) => written.push(fp),
}
}
Ok(())
}
fn read_input(fp: &OsString) -> Result<String> {
use std::io::Read;
let mut s = String::new();
if fp == "-" {
std::io::stdin().lock().read_to_string(&mut s)?;
} else {
std::fs::File::open(fp)?.read_to_string(&mut s)?;
};
Ok(s)
}
pub struct Formatter {
pub prefix: String,
pub suffix_digits: usize,
pub wd: std::path::PathBuf,
}
impl Default for Formatter {
fn default() -> Self {
Formatter {
prefix: "xx".to_string(),
suffix_digits: 2,
wd: std::env::current_dir().unwrap(),
}
}
}
impl Formatter {
pub fn path(&self, i: usize) -> String {
let f = (&self.wd).join(self.filename(i));
f.to_str().unwrap().to_string()
}
pub fn filename(&self, i: usize) -> String { format!("{}{2:01$}", self.prefix, self.suffix_digits, i) }
pub fn new(prefix: String, suffix_digits: usize) -> std::io::Result<Self> {
Ok(Formatter {
prefix,
suffix_digits,
wd: std::env::current_dir()?,
})
}
}
pub struct Opt {
pub patterns: Vec<patterns::Pattern>,
pub formatter: Formatter,
pub text: String,
pub cleanup_after_error: bool,
pub write_file_sizes_to_stdout: bool,
}
impl<'a> Opt {
pub fn from_args() -> Result<Self> {
let opts = UserOpts::from_args();
Ok(Opt {
formatter: Formatter::new(opts.prefix, opts.suffix_digits)?,
patterns: patterns::build_from_args(&opts.patterns)?,
cleanup_after_error: !opts.no_remove_on_error_or_signal,
write_file_sizes_to_stdout: !opts.no_write_size,
text: read_input(&opts.file)?,
})
}
}
#[derive(Debug, structopt::StructOpt, PartialEq, Clone)]
pub struct UserOpts {
#[structopt(short = "f", long = "prefix", default_value = "xx")]
prefix: String,
#[structopt(short = "k")]
no_remove_on_error_or_signal: bool,
#[structopt(short = "n", long = "number", default_value = "2")]
suffix_digits: usize,
#[structopt(short = "s")]
no_write_size: bool,
#[structopt(parse(from_os_str))]
file: OsString,
#[structopt(parse(from_os_str))]
patterns: Vec<OsString>,
}