From 4914e6a56547dcbe0fdbcfd7eae62a3d416875a5 Mon Sep 17 00:00:00 2001 From: Till Hoeppner Date: Sun, 26 Jul 2015 23:56:52 +0200 Subject: Add glob-ing for input files --- Cargo.toml | 1 + src/chain.rs | 48 ++++++++++++++++++++ src/format/mod.rs | 4 +- src/main.rs | 128 ++++++++++++++++++++++++++++++++++++------------------ 4 files changed, 136 insertions(+), 45 deletions(-) create mode 100644 src/chain.rs diff --git a/Cargo.toml b/Cargo.toml index 59c31f5..4b87c58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ log = "*" env_logger = "*" bincode = "*" combine = "*" +glob = "*" [profile.release] opt-level = 3 diff --git a/src/chain.rs b/src/chain.rs new file mode 100644 index 0000000..9b9848a --- /dev/null +++ b/src/chain.rs @@ -0,0 +1,48 @@ +use std::io::{ Result, Read, Write }; + +pub struct Chain { + elem: Vec, + index: usize +} + +impl Read for Chain { + fn read(&mut self, buf: &mut [u8]) -> Result { + loop { + match self.elem.get_mut(self.index) { + Some(ref mut r) => match try!(r.read(buf)) { + 0 => self.index += 1, + n => return Ok(n) + }, + None => return Ok(0) + } + } + } +} + +impl Write for Chain { + fn write(&mut self, buf: &[u8]) -> Result { + loop { + match self.elem.get_mut(self.index) { + Some(ref mut r) => match try!(r.write(buf)) { + 0 => self.index += 1, + n => return Ok(n) + }, + None => return Ok(0) + } + } + } + + fn flush(&mut self) -> Result<()> { + match self.elem.get_mut(self.index) { + Some(ref mut r) => r.flush(), + None => Ok(()) + } + } + +} + +impl Chain { + pub fn new(elem: Vec) -> Chain { + Chain { index: 0, elem: elem } + } +} diff --git a/src/format/mod.rs b/src/format/mod.rs index cd849d5..558de90 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -48,7 +48,7 @@ impl<'a, T, I: BufRead> DecodeBox<'a, I> for T where T: Decode<'a, I> { } } -pub fn decoder(format: &str) -> Option>> { +pub fn decoder<'a>(format: &str) -> Option>> { match format { "energymech" => Some(Box::new(energymech::Energymech)), "weechat3" => Some(Box::new(weechat3::Weechat3)), @@ -57,7 +57,7 @@ pub fn decoder(format: &str) -> Option>> { } } -pub fn encoder(format: &str) -> Option>> { +pub fn encoder<'a>(format: &str) -> Option>> { match format { "energymech" => Some(Box::new(energymech::Energymech)), "weechat3" => Some(Box::new(weechat3::Weechat3)), diff --git a/src/main.rs b/src/main.rs index a566087..81cfbd9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,9 +24,10 @@ extern crate regex; #[macro_use] extern crate log; extern crate env_logger; +extern crate glob; use std::process; -use std::io::{ self, BufReader }; +use std::io::{ self, Read, BufRead, BufReader, Write, BufWriter }; use std::fs::File; use std::error::Error; use std::str::FromStr; @@ -37,10 +38,14 @@ use docopt::Docopt; use chrono::offset::fixed::FixedOffset; use chrono::naive::date::NaiveDate; +use glob::glob; + use ilc::context::Context; use ilc::format::{ self, Encode, Decode, DecodeBox }; use ilc::event::{ Event, Type }; +mod chain; + static USAGE: &'static str = r#" d8b 888 Y8P 888 @@ -54,17 +59,21 @@ Y8P 888 A converter and statistics utility for IRC log files. Usage: - ilc parse ... - ilc convert [--date DATE] [--tz SECS] [--channel CH] - ilc freq + ilc parse [options] [-i FILE...] + ilc convert [options] [-i FILE...] + ilc freq [options] [-i FILE...] ilc (-h | --help | -v | --version) Options: -h --help Show this screen. -v --version Show the version (duh). --date DATE Override the date for this log. ISO 8601, YYYY-MM-DD. - --tz SECONDS UTC offset in the direction of the western hemisphere. [default: 0] + --tz SECONDS UTC offset in the direction of the western hemisphere. --channel CH Set a channel for the given log. + --inf INF Set the input format. + --outf OUTF Set the output format. + --in -i IN Give an input file, instead of stdin. + --out -o OUT Give an output file, instead of stdout. "#; #[derive(RustcDecodable, Debug)] @@ -73,25 +82,54 @@ struct Args { cmd_convert: bool, cmd_freq: bool, arg_file: Vec, - arg_informat: Option, - arg_outformat: Option, + flag_in: Vec, + flag_out: Option, + flag_inf: Option, + flag_outf: Option, flag_help: bool, flag_version: bool, flag_date: Option, - flag_tz: i32, + flag_tz: Option, flag_channel: Option } fn error(e: Box) -> ! { - println!("{}", e.description()); + let _ = writeln!(&mut io::stderr(), "Error: {}", e); let mut e = e.cause(); while let Some(err) = e { - println!("\t{}", err.description()); + let _ = writeln!(&mut io::stderr(), "\t{}", err); e = err.cause(); } process::exit(1) } +fn die(s: &str) -> ! { + let _ = writeln!(&mut io::stderr(), "Aborting: {}", s); + process::exit(1) +} + +fn force_decoder<'a>(s: Option) -> Box> { + let inf = match s { + Some(s) => s, + None => die("You didn't specify the input format") + }; + match format::decoder(&inf) { + Some(d) => d, + None => die(&format!("The format `{}` is unknown to me", inf)) + } +} + +fn force_encoder<'a>(s: Option) -> Box> { + let outf = match s { + Some(s) => s, + None => die("You didn't specify the output format") + }; + match format::encoder(&outf) { + Some(e) => e, + None => die(&format!("The format `{}` is unknown to me", outf)) + } +} + fn main() { env_logger::init().unwrap(); let args: Args = Docopt::new(USAGE) @@ -103,44 +141,50 @@ fn main() { } let context = Context { - timezone: FixedOffset::west(args.flag_tz), + timezone: FixedOffset::west(args.flag_tz.and_then(|s| s.parse().ok()).unwrap_or(0)), override_date: args.flag_date.and_then(|d| NaiveDate::from_str(&d).ok()), channel: args.flag_channel.clone() }; - if args.cmd_parse { - let mut parser = format::energymech::Energymech; - let formatter = format::binary::Binary; - for file in args.arg_file { - let f: BufReader = BufReader::new(File::open(file).unwrap()); - let iter = parser.decode(&context, f); - for e in iter { - info!("Parsed: {:?}", e); - drop(formatter.encode(&context, io::stdout(), &e.unwrap())); - } - } - } - - if args.cmd_convert { - let stdin = io::stdin(); + let mut input: Box = if args.flag_in.len() > 0 { + let input_files = args.flag_in.iter() + .flat_map(|p| { + match glob(p) { + Ok(paths) => paths, + Err(e) => die(&format!("{}", e.msg)) + } + }).filter_map(Result::ok).map(|p| File::open(p).unwrap()).collect(); + Box::new(BufReader::new(chain::Chain::new(input_files))) + } else { + Box::new(BufReader::new(io::stdin())) + }; - let informat = args.arg_informat.expect("Must provide informat"); - let outformat = args.arg_outformat.expect("Must provide outformat"); - let mut decoder = format::decoder(&informat).expect("Decoder not available"); - let encoder = format::encoder(&outformat).expect("Encoder not available"); + let mut output: Box = if let Some(out) = args.flag_out { + match File::create(out) { + Ok(f) => Box::new(BufWriter::new(f)), + Err(e) => error(Box::new(e)) + } + } else { + Box::new(BufWriter::new(io::stdout())) + }; - let mut lock = stdin.lock(); - for e in decoder.decode_box(&context, &mut lock) { + if args.cmd_parse { + let mut decoder = force_decoder(args.flag_inf); + let encoder = force_encoder(args.flag_outf); + for e in decoder.decode_box(&context, &mut input) { + info!("Parsed: {:?}", e); + let _ = encoder.encode(&context, &mut output, &e.unwrap()); + } + } else if args.cmd_convert { + let mut decoder = force_decoder(args.flag_inf); + let encoder = force_encoder(args.flag_outf); + for e in decoder.decode_box(&context, &mut input) { match e { - Ok(e) => { - let _ = encoder.encode(&context, &mut io::stdout(), &e); - }, + Ok(e) => { let _ = encoder.encode(&context, &mut io::stdout(), &e); }, Err(e) => error(Box::new(e)) } } - } - - if args.cmd_freq { + } else if args.cmd_freq { struct Person { lines: u32, words: u32 @@ -158,12 +202,10 @@ fn main() { } } - let stdin = io::stdin(); - let mut stats: HashMap = HashMap::new(); - let mut parser = format::weechat3::Weechat3; - for e in parser.decode(&context, stdin.lock()) { + let mut decoder = force_decoder(args.flag_inf); + for e in decoder.decode_box(&context, &mut input) { let m = match e { Ok(m) => m, Err(err) => panic!(err) @@ -191,7 +233,7 @@ fn main() { stats.sort_by(|&(_, ref a), &(_, ref b)| b.words.cmp(&a.words)); for &(ref name, ref stat) in stats.iter().take(10) { - println!("{}:\n\tLines: {}\n\tWords: {}", name, stat.lines, stat.words) + let _ = write!(&mut output, "{}:\n\tLines: {}\n\tWords: {}\n", name, stat.lines, stat.words); } } } -- cgit v1.2.3