aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTill Hoeppner2015-07-26 23:56:52 +0200
committerTill Hoeppner2015-07-26 23:56:52 +0200
commit4914e6a56547dcbe0fdbcfd7eae62a3d416875a5 (patch)
tree3cf2fd6e9b0b0376a23a0ddb32c4242d6a6fec9b
parent6a817022a7301d89a2d93be400294f78b5dae0b4 (diff)
downloadilc-4914e6a56547dcbe0fdbcfd7eae62a3d416875a5.tar.gz
ilc-4914e6a56547dcbe0fdbcfd7eae62a3d416875a5.tar.xz
ilc-4914e6a56547dcbe0fdbcfd7eae62a3d416875a5.zip
Add glob-ing for input files
-rw-r--r--Cargo.toml1
-rw-r--r--src/chain.rs48
-rw-r--r--src/format/mod.rs4
-rw-r--r--src/main.rs128
4 files changed, 136 insertions, 45 deletions
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<T> {
+ elem: Vec<T>,
+ index: usize
+}
+
+impl<T: Read> Read for Chain<T> {
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+ 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<T: Write> Write for Chain<T> {
+ fn write(&mut self, buf: &[u8]) -> Result<usize> {
+ 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<T> Chain<T> {
+ pub fn new(elem: Vec<T>) -> Chain<T> {
+ 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<Box<DecodeBox<&mut BufRead>>> {
+pub fn decoder<'a>(format: &str) -> Option<Box<DecodeBox<'a, &'a mut BufRead>>> {
match format {
"energymech" => Some(Box::new(energymech::Energymech)),
"weechat3" => Some(Box::new(weechat3::Weechat3)),
@@ -57,7 +57,7 @@ pub fn decoder(format: &str) -> Option<Box<DecodeBox<&mut BufRead>>> {
}
}
-pub fn encoder(format: &str) -> Option<Box<Encode<&mut Write>>> {
+pub fn encoder<'a>(format: &str) -> Option<Box<Encode<'a, &'a mut Write>>> {
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 <file>...
- ilc convert <informat> <outformat> [--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<String>,
- arg_informat: Option<String>,
- arg_outformat: Option<String>,
+ flag_in: Vec<String>,
+ flag_out: Option<String>,
+ flag_inf: Option<String>,
+ flag_outf: Option<String>,
flag_help: bool,
flag_version: bool,
flag_date: Option<String>,
- flag_tz: i32,
+ flag_tz: Option<String>,
flag_channel: Option<String>
}
fn error(e: Box<Error>) -> ! {
- 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<String>) -> Box<DecodeBox<'a, &'a mut BufRead>> {
+ 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<String>) -> Box<Encode<'a, &'a mut Write>> {
+ 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<File> = 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<BufRead> = 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<Write> = 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<String, Person> = 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);
}
}
}