extern crate byteorder; extern crate chan; extern crate csv; extern crate csv_index; extern crate docopt; extern crate filetime; extern crate num_cpus; extern crate rand; extern crate regex; extern crate rustc_serialize; extern crate stats; extern crate tabwriter; extern crate threadpool; use std::borrow::ToOwned; use std::env; use std::fmt; use std::io; use std::process; use docopt::Docopt; macro_rules! wout { ($($arg:tt)*) => ({ use std::io::Write; (writeln!(&mut ::std::io::stdout(), $($arg)*)).unwrap(); }); } macro_rules! werr { ($($arg:tt)*) => ({ use std::io::Write; (writeln!(&mut ::std::io::stderr(), $($arg)*)).unwrap(); }); } macro_rules! fail { ($e:expr) => (Err(::std::convert::From::from($e))); } macro_rules! command_list { () => ( " cat Concatenate by row or column count Count records fixlengths Makes all records have same length flatten Show one field per line fmt Format CSV output (change field delimiter) frequency Show frequency tables headers Show header names help Show this usage message. index Create CSV index for faster access input Read CSV data with special quoting rules join Join CSV files sample Randomly sample CSV data search Search CSV data with regexes select Select columns from CSV slice Slice records from CSV sort Sort CSV data split Split CSV data into many files stats Compute basic statistics table Align CSV data into columns " ) } mod cmd; mod config; mod index; mod select; mod util; static USAGE: &'static str = concat!(" Usage: xsv [...] xsv [options] Options: --list List all commands available. -h, --help Display this message -h Display the command help message --version Print version info and exit Commands:", command_list!()); #[derive(RustcDecodable)] struct Args { arg_command: Option, flag_list: bool, } fn main() { let args: Args = Docopt::new(USAGE) .and_then(|d| d.options_first(true) .version(Some(util::version())) .decode()) .unwrap_or_else(|e| e.exit()); if args.flag_list { wout!(concat!("Installed commands:", command_list!())); return; } match args.arg_command { None => { werr!(concat!( "xsv is a suite of CSV command line utilities. Please choose one of the following commands:", command_list!())); process::exit(0); } Some(cmd) => { match cmd.run() { Ok(()) => process::exit(0), Err(CliError::Flag(err)) => err.exit(), Err(CliError::Csv(err)) => { werr!("{}", err); process::exit(1); } Err(CliError::Io(ref err)) if err.kind() == io::ErrorKind::BrokenPipe => { process::exit(0); } Err(CliError::Io(err)) => { werr!("{}", err); process::exit(1); } Err(CliError::Other(msg)) => { werr!("{}", msg); process::exit(1); } } } } } #[derive(Debug, RustcDecodable)] enum Command { Cat, Count, FixLengths, Flatten, Fmt, Frequency, Headers, Help, Index, Input, Join, Partition, Sample, Search, Select, Slice, Sort, Split, Stats, Table, } impl Command { fn run(self) -> CliResult<()> { let argv: Vec<_> = env::args().map(|v| v.to_owned()).collect(); let argv: Vec<_> = argv.iter().map(|s| &**s).collect(); let argv = &*argv; match self { Command::Cat => cmd::cat::run(argv), Command::Count => cmd::count::run(argv), Command::FixLengths => cmd::fixlengths::run(argv), Command::Flatten => cmd::flatten::run(argv), Command::Fmt => cmd::fmt::run(argv), Command::Frequency => cmd::frequency::run(argv), Command::Headers => cmd::headers::run(argv), Command::Help => { wout!("{}", USAGE); Ok(()) } Command::Index => cmd::index::run(argv), Command::Input => cmd::input::run(argv), Command::Join => cmd::join::run(argv), Command::Partition => cmd::partition::run(argv), Command::Sample => cmd::sample::run(argv), Command::Search => cmd::search::run(argv), Command::Select => cmd::select::run(argv), Command::Slice => cmd::slice::run(argv), Command::Sort => cmd::sort::run(argv), Command::Split => cmd::split::run(argv), Command::Stats => cmd::stats::run(argv), Command::Table => cmd::table::run(argv), } } } pub type CliResult = Result; #[derive(Debug)] pub enum CliError { Flag(docopt::Error), Csv(csv::Error), Io(io::Error), Other(String), } impl fmt::Display for CliError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { CliError::Flag(ref e) => { e.fmt(f) } CliError::Csv(ref e) => { e.fmt(f) } CliError::Io(ref e) => { e.fmt(f) } CliError::Other(ref s) => { f.write_str(&**s) } } } } impl From for CliError { fn from(err: docopt::Error) -> CliError { CliError::Flag(err) } } impl From for CliError { fn from(err: csv::Error) -> CliError { if !err.is_io_error() { return CliError::Csv(err); } match err.into_kind() { csv::ErrorKind::Io(v) => From::from(v), _ => unreachable!(), } } } impl From for CliError { fn from(err: io::Error) -> CliError { CliError::Io(err) } } impl From for CliError { fn from(err: String) -> CliError { CliError::Other(err) } } impl<'a> From<&'a str> for CliError { fn from(err: &'a str) -> CliError { CliError::Other(err.to_owned()) } } impl From for CliError { fn from(err: regex::Error) -> CliError { CliError::Other(format!("{:?}", err)) } }