aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTill Höppner2016-03-16 15:14:03 +0100
committerTill Höppner2016-03-16 15:14:03 +0100
commit586461676df9abd2e011fbcb1bb7c784cdb2844c (patch)
tree6c66fa86c9cb9876757b099d7460ff17c39195a5
parent363e3722256c8e91bbd78bfcb2b1d3ffc83d9c0a (diff)
downloadilc-586461676df9abd2e011fbcb1bb7c784cdb2844c.tar.gz
ilc-586461676df9abd2e011fbcb1bb7c784cdb2844c.tar.xz
ilc-586461676df9abd2e011fbcb1bb7c784cdb2844c.zip
Add filtering functionality
-rw-r--r--base/src/event.rs74
-rw-r--r--cli/Cargo.toml1
-rw-r--r--cli/src/lib.rs82
-rw-r--r--ops/Cargo.toml1
-rw-r--r--ops/src/lib.rs23
5 files changed, 149 insertions, 32 deletions
diff --git a/base/src/event.rs b/base/src/event.rs
index 745cc64..3176a9a 100644
--- a/base/src/event.rs
+++ b/base/src/event.rs
@@ -150,25 +150,83 @@ pub enum Type<'a> {
}
impl<'a> Type<'a> {
+ pub fn actor(&self) -> Option<&str> {
+ use self::Type::*;
+ match self {
+ &Msg { ref from, .. } => Some(from),
+ &Action { ref from, .. } => Some(from),
+ &Join { ref nick, .. } => Some(nick),
+ &Part { ref nick, .. } => Some(nick),
+ &Quit { ref nick, .. } => Some(nick),
+ &Nick { ref old_nick, .. } => Some(old_nick),
+ &Notice { ref from, .. } => Some(from),
+ &Kick { ref kicking_nick, .. } => kicking_nick.as_ref().map(|s| &*s as &str),
+ &TopicChange { ref nick, .. } => nick.as_ref().map(|s| &*s as &str),
+ &Mode { ref nick, .. } => nick.as_ref().map(|s| &*s as &str),
+ _ => None,
+ }
+ }
+
pub fn involves(&self, needle: &str) -> bool {
use self::Type::*;
match self {
- &Msg { ref from, .. } => from == needle,
- &Action { ref from, .. } => from == needle,
+ &Msg { ref from, ref content, .. } => from == needle || content.contains(needle),
+ &Action { ref from, ref content, .. } => from == needle || content.contains(needle),
&Join { ref nick, .. } => nick == needle,
- &Part { ref nick, .. } => nick == needle,
- &Quit { ref nick, .. } => nick == needle,
+ &Part { ref nick, ref reason, .. } => {
+ nick == needle || reason.as_ref().map_or(false, |r| r.contains(needle))
+ }
+ &Quit { ref nick, ref reason, .. } => {
+ nick == needle || reason.as_ref().map_or(false, |r| r.contains(needle))
+ }
&Nick { ref old_nick, ref new_nick, .. } => old_nick == needle || new_nick == needle,
- &Notice { ref from, .. } => from == needle,
- &Kick { ref kicked_nick, ref kicking_nick, .. } => {
+ &Notice { ref from, ref content, .. } => from == needle || content.contains(needle),
+ &Kick { ref kicked_nick, ref kicking_nick, ref kick_message, .. } => {
*kicked_nick == Cow::Borrowed(needle) ||
- kicking_nick.as_ref().map_or(false, |k| k.as_ref() == Cow::Borrowed(needle))
+ kicking_nick.as_ref().map_or(false, |k| k.as_ref() == Cow::Borrowed(needle)) ||
+ kick_message.as_ref().map_or(false, |k| k.as_ref() == Cow::Borrowed(needle))
+ }
+ &TopicChange { ref nick, ref new_topic, .. } => {
+ nick.as_ref().map_or(false, |k| k.as_ref() == needle) || new_topic.contains(needle)
}
- &TopicChange { ref nick, .. } => nick.as_ref().map_or(false, |k| k.as_ref() == needle),
&Mode { ref nick, .. } => {
nick.as_ref().map_or(false, |k| k.as_ref() == Cow::Borrowed(needle))
}
_ => false,
}
}
+
+ pub fn type_desc(&self) -> &'static str {
+ use self::Type::*;
+ match self {
+ &Msg { .. } => "message",
+ &Action { .. } => "action",
+ &Join { .. } => "join",
+ &Part { .. } => "part",
+ &Quit { .. } => "quit",
+ &Nick { .. } => "nick",
+ &Notice { .. } => "notice",
+ &Topic { .. } => "topic",
+ &TopicChange { .. } => "topic_change",
+ &Kick { .. } => "kick",
+ &Mode { .. } => "mode",
+ &Connect { .. } => "connect",
+ &Disconnect { .. } => "disconnect",
+ }
+ }
+
+ pub fn text(&self) -> Option<&str> {
+ use self::Type::*;
+ match self {
+ &Msg { ref content, .. } => Some(content),
+ &Action { ref content, .. } => Some(content),
+ &Part { ref reason, .. } => reason.as_ref().map(|s| s as &str),
+ &Quit { ref reason, .. } => reason.as_ref().map(|s| s as &str),
+ &Notice { ref content, .. } => Some(content),
+ &Kick { ref kick_message, .. } => kick_message.as_ref().map(|s| s as &str),
+ &Topic { ref topic, .. } => Some(topic),
+ &TopicChange { ref new_topic, .. } => Some(new_topic),
+ _ => None,
+ }
+ }
}
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index e958ef5..26006d3 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -15,6 +15,7 @@ default = ["ilc-format-weechat", "ilc-format-energymech"]
log = "0.3.5"
clap = "2.1.2"
chrono = "0.2.19"
+regex = "0.1.55"
serde = "~0.7"
serde_json = "~0.7"
env_logger = "0.3.2"
diff --git a/cli/src/lib.rs b/cli/src/lib.rs
index eb2fb43..ac31636 100644
--- a/cli/src/lib.rs
+++ b/cli/src/lib.rs
@@ -11,17 +11,21 @@ extern crate env_logger;
extern crate serde;
extern crate serde_json;
extern crate glob;
+extern crate regex;
use ilc_base::{Context, Decode, Encode};
+use ilc_ops::convert::{Filter, Operator, Subject};
use ilc_format_weechat::Weechat;
use ilc_format_energymech::Energymech;
-use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
+use clap::{App, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand};
use chrono::{FixedOffset, NaiveDate};
use glob::glob;
+use regex::Regex;
+
use std::str::FromStr;
use std::fs::File;
use std::path::{Path, PathBuf};
@@ -30,6 +34,7 @@ use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::process;
use std::error::Error;
+
mod chain;
mod stats;
@@ -121,7 +126,32 @@ pub fn main(cli: Cli) {
.setting(AppSettings::AllowLeadingHyphen))
.subcommand(SubCommand::with_name("convert")
.about("Convert from a source to a target format")
- .setting(AppSettings::AllowLeadingHyphen))
+ .setting(AppSettings::AllowLeadingHyphen)
+ .arg(Arg::with_name("subject")
+ .takes_value(true)
+ .requires("operator")
+ .long("if")
+ .possible_values(&["nick", "time", "type", "text"]))
+ .arg(Arg::with_name("op_not").long("not"))
+ .arg(Arg::with_name("op_exactly")
+ .takes_value(true)
+ .long("exactly"))
+ .arg(Arg::with_name("op_contains")
+ .takes_value(true)
+ .long("contains"))
+ .arg(Arg::with_name("op_greater")
+ .takes_value(true)
+ .long("greater"))
+ .arg(Arg::with_name("op_less").takes_value(true).long("less"))
+ .arg(Arg::with_name("op_matches")
+ .takes_value(true)
+ .long("matches"))
+ .group(ArgGroup::with_name("operator").args(&["op_exactly",
+ "op_contains",
+ "op_equal",
+ "op_greater",
+ "op_less",
+ "op_matches"])))
.subcommand(SubCommand::with_name("stats")
.about("Analyse the activity of users by certain metrics")
.setting(AppSettings::AllowLeadingHyphen))
@@ -157,11 +187,57 @@ pub fn main(cli: Cli) {
}
("convert", Some(args)) => {
let e = Environment(&args);
+ let subject = match args.value_of("subject") {
+ Some("nick") => Some(Subject::Nick),
+ Some("time") => Some(Subject::Time),
+ Some("type") => Some(Subject::Type),
+ Some("text") => Some(Subject::Text),
+ _ => None,
+ };
+
+ let op = {
+ if args.is_present("operator") {
+ if let Some(sub) = args.value_of("op_exactly") {
+ Some(Operator::Exactly(sub.into()))
+ } else if let Some(sub) = args.value_of("op_contains") {
+ Some(Operator::Contains(sub.into()))
+ } else if let Some(sub) = args.value_of("op_matches") {
+ match Regex::new(sub) {
+ Ok(regex) => Some(Operator::Matches(regex)),
+ Err(e) => error(Box::new(e)),
+ }
+ } else {
+ // must be numeric operator if not op_exactly, op_contains, or op_matches
+ // unwrap is safe because of .is_present("operator") earlier
+ let num = match args.value_of("operator").unwrap().parse::<i64>() {
+ Ok(n) => n,
+ Err(e) => error(Box::new(e)),
+ };
+
+ if args.is_present("op_equal") {
+ Some(Operator::Equal(num))
+ } else if args.is_present("op_greater") {
+ Some(Operator::Greater(num))
+ } else if args.is_present("op_less") {
+ Some(Operator::Less(num))
+ } else {
+ None
+ }
+ }
+ } else {
+ None
+ }
+ };
+
+ let filter = subject.and_then(|s| op.map(|o| Filter(s, o)));
+
ilc_ops::convert::convert(&e.context(),
&mut e.input(),
&mut *e.decoder(),
&mut *e.output(),
- &*e.encoder())
+ &*e.encoder(),
+ filter,
+ args.is_present("op_not"))
}
("stats", Some(args)) => {
let e = Environment(&args);
diff --git a/ops/Cargo.toml b/ops/Cargo.toml
index 6789aa2..85ca5a7 100644
--- a/ops/Cargo.toml
+++ b/ops/Cargo.toml
@@ -10,6 +10,7 @@ authors = ["Till Höppner <till@hoeppner.ws>"]
[dependencies]
log = "0.3.5"
chrono = "0.2.19"
+regex = "0.1.54"
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 2ebe51f..6fd4cab 100644
--- a/ops/src/lib.rs
+++ b/ops/src/lib.rs
@@ -5,9 +5,11 @@ extern crate bit_set;
extern crate serde;
extern crate chrono;
extern crate ilc_base;
+extern crate regex;
mod ageset;
pub mod stats;
+pub mod convert;
/// No-op log parsing
pub mod parse {
@@ -27,27 +29,6 @@ pub mod parse {
}
}
-/// Log format conversion
-pub mod convert {
- use ilc_base::{self, Context, Decode, Encode};
- use std::io::{BufRead, Write};
-
- /// Convert from one format to another, not necessarily different, format. In combination with a
- /// timezone offset, this can be used to correct the timestamps.
- /// Will return `Err` and abort conversion if the decoder yields `Err` or re-encoding fails.
- pub fn convert(ctx: &Context,
- input: &mut BufRead,
- decoder: &mut Decode,
- output: &mut Write,
- encoder: &Encode)
- -> ilc_base::Result<()> {
- for e in decoder.decode(&ctx, input) {
- try!(encoder.encode(&ctx, output, &try!(e)));
- }
- Ok(())
- }
-}
-
/// Last-seen of nicks
pub mod seen {
use ilc_base::{self, Context, Decode, Encode, Event};