aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/src/lib.rs54
-rw-r--r--ops/Cargo.toml1
-rw-r--r--ops/src/lib.rs1
-rw-r--r--ops/src/stats.rs26
-rw-r--r--src/lambda.rs18
-rw-r--r--src/lazy.rs74
-rw-r--r--warning.rs11
7 files changed, 64 insertions, 121 deletions
diff --git a/cli/src/lib.rs b/cli/src/lib.rs
index 8eacea4..eb2fb43 100644
--- a/cli/src/lib.rs
+++ b/cli/src/lib.rs
@@ -52,15 +52,17 @@ pub fn main(cli: Cli) {
let args = App::new("ilc")
.version(&version[..])
.setting(AppSettings::GlobalVersion)
+ .setting(AppSettings::AllowLeadingHyphen)
+ .setting(AppSettings::UnifiedHelpMessage)
.setting(AppSettings::VersionlessSubcommands)
.setting(AppSettings::ArgRequiredElseHelp)
.author("Till Höppner <till@hoeppner.ws>")
.about("A converter and statistics utility for IRC log files")
- .arg(Arg::with_name("timezone")
- .help("UTC offset in the direction of the western hemisphere")
+ .arg(Arg::with_name("time")
+ .help("Timestamp offset, in seconds")
.global(true)
.takes_value(true)
- .long("timezone")
+ .long("timeoffset")
.short("t"))
.arg(Arg::with_name("date")
.help("Override the date for this log, ISO 8601, YYYY-MM-DD")
@@ -101,6 +103,7 @@ pub fn main(cli: Cli) {
.global(true)
.takes_value(true)
.multiple(true)
+ .number_of_values(1)
.long("input")
.short("i"))
.arg(Arg::with_name("output_file")
@@ -114,24 +117,32 @@ pub fn main(cli: Cli) {
.takes_value(false)
.long("notice"))
.subcommand(SubCommand::with_name("parse")
- .about("Parse the input, checking the format"))
+ .about("Parse the input, checking the format")
+ .setting(AppSettings::AllowLeadingHyphen))
.subcommand(SubCommand::with_name("convert")
- .about("Convert from a source to a target format"))
+ .about("Convert from a source to a target format")
+ .setting(AppSettings::AllowLeadingHyphen))
.subcommand(SubCommand::with_name("stats")
- .about("Analyse the activity of users by certain metrics"))
+ .about("Analyse the activity of users by certain metrics")
+ .setting(AppSettings::AllowLeadingHyphen))
.subcommand(SubCommand::with_name("seen")
.about("Print the last line a nick was active")
+ .setting(AppSettings::AllowLeadingHyphen)
.arg(Arg::with_name("nick")
.help("The nick you're looking for")
.takes_value(true)
.required(true)
.index(1)))
- .subcommand(SubCommand::with_name("sort").about("Sorts a log by time"))
+ .subcommand(SubCommand::with_name("sort")
+ .about("Sorts a log by time")
+ .setting(AppSettings::AllowLeadingHyphen))
.subcommand(SubCommand::with_name("dedup")
- .about("Removes duplicate log entries in close proximity"))
+ .about("Removes duplicate log entries in close proximity")
+ .setting(AppSettings::AllowLeadingHyphen))
.subcommand(SubCommand::with_name("merge")
.about("Merges the input logs. This has to keep everything \
- in memory"))
+ in memory")
+ .setting(AppSettings::AllowLeadingHyphen))
.get_matches();
if args.is_present("notice") {
@@ -223,6 +234,21 @@ pub fn die(s: &str) -> ! {
process::exit(1)
}
+macro_rules! error {
+ ($code: expr, $fmt:expr) => {{
+ use std::io::Write;
+ let err = std::io::stderr();
+ let _ = writeln!(&mut err.lock(), $fmt);
+ std::process::exit($code);
+ }};
+ ($code: expr, $fmt:expr, $($arg:tt)*) => {{
+ use std::io::Write;
+ let err = std::io::stderr();
+ let _ = writeln!(&mut err.lock(), $fmt, $($arg)*);
+ std::process::exit($code);
+ }};
+}
+
pub fn decoder(format: &str) -> Option<Box<Decode>> {
match format {
"energymech" | "em" => Some(Box::new(Energymech)),
@@ -252,7 +278,7 @@ pub fn force_decoder(s: Option<&str>) -> Box<Decode> {
};
match decoder(&inf) {
Some(d) => d,
- None => die(&format!("The format `{}` is unknown to me", inf)),
+ None => error!(2, "The format `{}` is unknown to me", inf),
}
}
@@ -263,7 +289,7 @@ pub fn force_encoder<'a>(s: Option<&str>) -> Box<Encode> {
};
match encoder(&outf) {
Some(e) => e,
- None => die(&format!("The format `{}` is unknown to me", outf)),
+ None => error!(2, "The format `{}` is unknown to me", outf),
}
}
@@ -306,8 +332,8 @@ impl<'a> Environment<'a> {
pub fn build_context(args: &ArgMatches) -> Context {
let mut context = Context {
- timezone: FixedOffset::west(args.value_of("timezone")
- .and_then(|s| s.parse().ok())
+ timezone: FixedOffset::west(args.value_of("time")
+ .and_then(|s| s.parse::<i32>().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(),
@@ -325,7 +351,7 @@ pub fn build_context(args: &ArgMatches) -> Context {
context.override_date = Some(date);
}
}
- _n => die("Too many input files, can't infer date"),
+ n => error!(3, "Too many input files ({}), can't infer date", n),
}
}
context
diff --git a/ops/Cargo.toml b/ops/Cargo.toml
index f4bf082..6789aa2 100644
--- a/ops/Cargo.toml
+++ b/ops/Cargo.toml
@@ -9,6 +9,7 @@ authors = ["Till Höppner <till@hoeppner.ws>"]
[dependencies]
log = "0.3.5"
+chrono = "0.2.19"
ilc-base = "~0.2"
blist = "0.0.4"
bit-set = "0.3.0"
diff --git a/ops/src/lib.rs b/ops/src/lib.rs
index d5aa003..2ebe51f 100644
--- a/ops/src/lib.rs
+++ b/ops/src/lib.rs
@@ -3,6 +3,7 @@ extern crate log;
extern crate blist;
extern crate bit_set;
extern crate serde;
+extern crate chrono;
extern crate ilc_base;
mod ageset;
diff --git a/ops/src/stats.rs b/ops/src/stats.rs
index 49f4068..7e37a80 100644
--- a/ops/src/stats.rs
+++ b/ops/src/stats.rs
@@ -1,15 +1,21 @@
//! Per-nick word/line statistics
-
-use ilc_base::{self, Context, Decode, Event};
+use ilc_base::{self, Context, Decode, Event, Time};
use ilc_base::event::Type;
use std::collections::HashMap;
use std::io::BufRead;
+use chrono::{Datelike, NaiveDateTime, Timelike};
+
use serde::ser::{MapVisitor, Serialize, Serializer};
+pub type Day = [u32; 24];
+/// Weeks start on mondays.
+pub type Week = [Day; 7];
+
pub struct Stats {
pub freqs: HashMap<String, NickStat>,
+ pub week: Week,
}
impl Serialize for Stats {
@@ -22,6 +28,7 @@ impl Serialize for Stats {
where S: Serializer
{
try!(s.serialize_struct_elt("freqs", &self.0.freqs));
+ try!(s.serialize_struct_elt("week", &self.0.week));
Ok(None)
}
@@ -91,11 +98,19 @@ fn strip_nick(s: &str) -> &str {
/// Return all active nicks, with lines, words and words per lines counted.
pub fn stats(ctx: &Context, input: &mut BufRead, decoder: &mut Decode) -> ilc_base::Result<Stats> {
let mut freqs: HashMap<String, NickStat> = HashMap::new();
+ let mut week: Week = [[0; 24]; 7];
for e in decoder.decode(&ctx, input) {
let m = try!(e);
match m {
- Event { ty: Type::Msg { ref from, ref content, .. }, .. } => {
+ Event { ty: Type::Msg { ref from, ref content, .. }, ref time, .. } => {
+ if let &Time::Timestamp(stamp) = time {
+ let date = NaiveDateTime::from_timestamp(stamp, 0);
+ let dow = date.weekday().num_days_from_monday() as usize;
+ let hour = date.hour() as usize;
+ week[dow][hour] += 1;
+ }
+
let nick = strip_nick(from);
if freqs.contains_key(nick) {
let p: &mut NickStat = freqs.get_mut(nick).unwrap();
@@ -119,5 +134,8 @@ pub fn stats(ctx: &Context, input: &mut BufRead, decoder: &mut Decode) -> ilc_ba
}
}
- Ok(Stats { freqs: freqs })
+ Ok(Stats {
+ freqs: freqs,
+ week: week,
+ })
}
diff --git a/src/lambda.rs b/src/lambda.rs
deleted file mode 100644
index cf7b1ec..0000000
--- a/src/lambda.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// I'm only a little sorry for this.
-
-// Inline definition of anonymous functions. Examples:
-// l!(42;)
-// l!(i32 := 42)
-// l!(i: i32 := i + 42)
-// l!(i: i32, j: i32 -> i32 := i + j)
-#[macro_export]
-macro_rules! l {
- ($body: expr) => ({ fn f() { $body } f });
- ($res: ty := $body: expr) => ({ fn f() -> $res { $body } f });
- ($($n: ident: $t: ty),+ := $body: expr) => ({
- fn f($($n: $t),+) { $body } f
- });
- ($($n: ident: $t: ty),+ -> $res: ty := $body: expr) => ({
- fn f($($n: $t),+) -> $res { $body } f
- })
-}
diff --git a/src/lazy.rs b/src/lazy.rs
deleted file mode 100644
index b4f801b..0000000
--- a/src/lazy.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-#![macro_escape]
-
-use std::ptr;
-use std::cell::UnsafeCell;
-use std::ops::Deref;
-use std::boxed::FnBox;
-
-pub enum State<V> {
- Evaluated(V),
- Evaluating,
- Unevaluated(Box<FnBox() -> V>)
-}
-
-pub struct Lazy<V> {
- state: UnsafeCell<State<V>>,
-}
-
-impl<V> Lazy<V> {
- pub fn new(f: Box<FnBox() -> V>) -> Lazy<V> {
- Lazy { state: UnsafeCell::new(State::Unevaluated(f)) }
- }
-
- pub fn ready(v: V) -> Lazy<V> {
- Lazy { state: UnsafeCell::new(State::Evaluated(v)) }
- }
-
- pub fn force(&mut self) {
- self.value();
- }
-
- pub fn unwrap(self) -> V {
- unsafe {
- match self.state.into_inner() {
- State::Unevaluated(f) => f(),
- State::Evaluating => panic!("Illegal state, can't call unwrap during evaluation"),
- State::Evaluated(v) => v
- }
- }
- }
-
- pub fn value(&self) -> &V {
- unsafe {
- let state = self.state.get();
- match *state {
- State::Evaluated(ref v) => v,
- State::Evaluating => panic!("Illegal state, can't call value during evaluation"),
- State::Unevaluated(_) => {
- if let State::Unevaluated(f) = ptr::replace(state, State::Evaluating) {
- ptr::replace(state, State::Evaluated(f()));
- }
- if let State::Evaluated(ref v) = *state { return v }
- unreachable!()
- }
- }
- }
- }
-}
-
-impl<V> Deref for Lazy<V> {
- type Target = V;
- fn deref(&self) -> &V { self.value() }
-}
-
-#[macro_export]
-macro_rules! lazy {
- (@as_expr $val: expr) => { $val };
- ($($val: tt)*) => { Lazy::new(Box::new(move || lazy![@as_expr { $($val)* }])) };
-}
-
-#[macro_export]
-macro_rules! eager {
- (@as_expr $val: expr) => { $val };
- ($($val: tt)*) => { Lazy::<_>::ready(eager![@as_expr { $($val)* }]) };
-}
diff --git a/warning.rs b/warning.rs
deleted file mode 100644
index a71bc3f..0000000
--- a/warning.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use std::iter;
-
-fn main() {
- let mut v = (0..20)
- .map(|_| iter::empty::<()>())
- .collect::<Vec<_>>();
-
- for i in 1..8 {
- v.remove(i + 1);
- }
-}