From cadd814c28b03205c8277530ef09bffcdba44ec6 Mon Sep 17 00:00:00 2001 From: Till Höppner Date: Wed, 3 Feb 2016 03:38:32 +0100 Subject: Docopt -> clap, main.rs -> src/app Modularise the old main function, switch to clap for easier addition of CLI arguments --- src/app/mod.rs | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 src/app/mod.rs (limited to 'src/app/mod.rs') diff --git a/src/app/mod.rs b/src/app/mod.rs new file mode 100644 index 0000000..50117e7 --- /dev/null +++ b/src/app/mod.rs @@ -0,0 +1,226 @@ +use clap::ArgMatches; + +use chrono::offset::fixed::FixedOffset; +use chrono::naive::date::NaiveDate; + +use glob::glob; + +use std::process; +use std::str::FromStr; +use std::path::PathBuf; +use std::io::{ self, Write, BufWriter, BufRead, BufReader }; +use std::fs::File; +use std::error::Error; + +use ilc::context::Context; +use ilc::format::{ self, Encode, Decode }; + +use ::chain; + +pub mod freq; + +pub fn error(e: Box) -> ! { + let _ = writeln!(&mut io::stderr(), "Error: {}", e); + let mut e = e.cause(); + while let Some(err) = e { + let _ = writeln!(&mut io::stderr(), "\t{}", err); + e = err.cause(); + } + process::exit(1) +} + +pub fn die(s: &str) -> ! { + let _ = writeln!(&mut io::stderr(), "Aborting: {}", s); + process::exit(1) +} + +pub fn force_decoder(s: Option<&str>) -> 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)) + } +} + +pub fn force_encoder<'a>(s: Option<&str>) -> 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)) + } +} + +pub fn build_context(args: &ArgMatches) -> Context { + Context { + timezone: FixedOffset::west(args.value_of("timezone").and_then(|s| s.parse().ok()).unwrap_or(0)), + override_date: args.value_of("date").and_then(|d| NaiveDate::from_str(&d).ok()), + channel: args.value_of("channel").map(str::to_owned).clone() + } +} + +pub fn build_input(args: &ArgMatches) -> Box { + let input_files = args.values_of("input_files"); + if input_files.map(|files| files.count() > 0).unwrap_or(false) { + let input_files: Vec = if let Some(iter) = args.values_of("input_files") { + iter.flat_map(|p| { + match glob(p) { + Ok(paths) => paths, + Err(e) => die(&format!("{}", e.msg)) + } + }).filter_map(Result::ok).collect() + } else { Vec::new() }; + + /*if args.flag_infer_date { + if input_files.len() > 1 { die("Too many input files, can't infer date") } + if let Some(date) = input_files.iter().next() + .map(PathBuf::as_path) + .and_then(Path::file_stem) + .and_then(OsStr::to_str) + .and_then(|s: &str| NaiveDate::from_str(s).ok()) { + context.override_date = Some(date); + } + }*/ + + Box::new(BufReader::new(chain::Chain::new(input_files.iter().map(|p| File::open(p).unwrap()).collect()))) + } else { + Box::new(BufReader::new(io::stdin())) + } +} + +pub fn build_output(args: &ArgMatches) -> Box { + if let Some(out) = args.value_of("output_file") { + match File::create(out) { + Ok(f) => Box::new(BufWriter::new(f)), + Err(e) => error(Box::new(e)) + } + } else { + Box::new(BufWriter::new(io::stdout())) + } +} + +pub struct Environment<'a>(pub &'a ArgMatches<'a>); + +impl<'a> Environment<'a> { + pub fn context(&self) -> Context { build_context(self.0) } + pub fn input(&self) -> Box { build_input(self.0) } + pub fn output(&self) -> Box { build_output(self.0) } + pub fn decoder(&self) -> Box { force_decoder(self.0.value_of("input_format")) } + pub fn encoder(&self) -> Box { force_encoder(self.0.value_of("output_format")) } +} + +pub mod parse { + use clap::ArgMatches; + use super::*; + pub fn parse(args: &ArgMatches) { + let env = Environment(args); + let (context, mut decoder, mut input) = (env.context(), env.decoder(), env.input()); + for e in decoder.decode(&context, &mut input) { + match e { + Err(e) => { println!("Foo!"); error(Box::new(e)) }, + _ => () + } + } + } +} + +pub mod convert { + use clap::ArgMatches; + use super::*; + pub fn convert(args: &ArgMatches) { + let env = Environment(args); + let (context, mut decoder, mut input, encoder, mut output) = + (env.context(), env.decoder(), env.input(), env.encoder(), env.output()); + + for e in decoder.decode(&context, &mut input) { + match e { + Ok(e) => { let _ = encoder.encode(&context, &mut output, &e); }, + Err(e) => error(Box::new(e)) + } + } + } +} + +pub mod seen { + use clap::ArgMatches; + use ilc::event::Event; + use ilc::format::{ self, Encode }; + use super::*; + pub fn seen(args: &ArgMatches) { + let env = Environment(args); + let (context, mut decoder, mut input, mut output) = (env.context(), env.decoder(), env.input(), env.output()); + + let nick = args.value_of("nick").expect("Required argument not present"); + + let mut last: Option = None; + for e in decoder.decode(&context, &mut input) { + let m = match e { + Ok(m) => m, + Err(err) => error(Box::new(err)) + }; + + if m.ty.involves(nick) + && last.as_ref().map_or(true, |last| m.time.as_timestamp() > last.time.as_timestamp()) { last = Some(m) } + } + let encoder = format::weechat3::Weechat3; + if let Some(ref m) = last { + let _ = encoder.encode(&context, &mut output, m); + } + } +} + +pub mod sort { + use clap::ArgMatches; + use ilc::event::Event; + use super::*; + pub fn sort(args: &ArgMatches) { + let env = Environment(args); + let (context, mut decoder, mut input, encoder, mut output) = + (env.context(), env.decoder(), env.input(), env.encoder(), env.output()); + + let mut events: Vec = decoder.decode(&context, &mut input) + .flat_map(Result::ok) + .collect(); + + events.sort_by(|a, b| a.time.cmp(&b.time)); + for e in events { + let _ = encoder.encode(&context, &mut output, &e); + } + } +} + +pub mod dedup { + use clap::ArgMatches; + use ilc::event::NoTimeHash; + use ::ageset::AgeSet; + use super::*; + pub fn dedup(args: &ArgMatches) { + let env = Environment(args); + let (context, mut decoder, mut input, encoder, mut output) = + (env.context(), env.decoder(), env.input(), env.encoder(), env.output()); + + let mut backlog = AgeSet::new(); + + for e in decoder.decode(&context, &mut input) { + if let Ok(e) = e { + let newest_event = e.clone(); + backlog.prune(move |a: &NoTimeHash| { + let age = newest_event.time.as_timestamp() - a.0.time.as_timestamp(); + age > 5000 + }); + // write `e` if it's a new event + let n = NoTimeHash(e); + if !backlog.contains(&n) { + let _ = encoder.encode(&context, &mut output, &n.0); + backlog.push(n); + } + } + } + + } +} -- cgit v1.2.3