diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ageset.rs | 47 | ||||
-rw-r--r-- | src/app/freq.rs | 93 | ||||
-rw-r--r-- | src/app/mod.rs | 271 | ||||
-rw-r--r-- | src/chain.rs | 54 | ||||
-rw-r--r-- | src/context.rs | 9 | ||||
-rw-r--r-- | src/event.rs | 201 | ||||
-rw-r--r-- | src/format/binary.rs | 57 | ||||
-rw-r--r-- | src/format/energymech.rs | 255 | ||||
-rw-r--r-- | src/format/irssi.rs | 205 | ||||
-rw-r--r-- | src/format/mod.rs | 100 | ||||
-rw-r--r-- | src/format/msgpack.rs | 62 | ||||
-rw-r--r-- | src/format/weechat.rs | 235 | ||||
-rw-r--r-- | src/lib.rs | 92 |
13 files changed, 0 insertions, 1681 deletions
diff --git a/src/ageset.rs b/src/ageset.rs deleted file mode 100644 index c97240f..0000000 --- a/src/ageset.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::collections::HashSet; -use std::hash::Hash; - -use blist::BList; - -/// So... this is a rather weird thing. -/// It allows to semi-efficiently check the oldest (earliest insertion) -/// elements for certain criteria and remove them in the order of insertion -/// if the criteria is met. -pub struct AgeSet<T> { - fifo: BList<T>, - set: HashSet<T>, -} - -impl<T> AgeSet<T> - where T: Eq + Hash + Clone -{ - pub fn new() -> Self { - AgeSet { - fifo: BList::new(), - set: HashSet::new(), - } - } - - pub fn contains(&self, t: &T) -> bool { - self.set.contains(t) - } - - pub fn prune<F>(&mut self, kill: F) - where F: Fn(&T) -> bool - { - while let Some(ref e) = self.fifo.front().map(T::clone) { - if kill(&e) { - let removed = self.fifo.pop_front().unwrap(); - self.set.remove(&e); - assert!(*e == removed); - } else { - break; - } - } - } - - pub fn push(&mut self, t: T) { - self.fifo.push_back(t.clone()); - self.set.insert(t); - } -} diff --git a/src/app/freq.rs b/src/app/freq.rs deleted file mode 100644 index 88a8e1f..0000000 --- a/src/app/freq.rs +++ /dev/null @@ -1,93 +0,0 @@ -use clap::ArgMatches; - -use std::collections::HashMap; - -use ilc::event::{Event, Type}; - -use super::*; - -struct Person { - lines: u32, - alpha_lines: u32, - words: u32, -} - -fn words_alpha(s: &str) -> (u32, bool) { - let mut alpha = false; - let mut words = 0; - for w in s.split_whitespace() { - if !w.is_empty() { - words += 1; - if w.chars().any(char::is_alphabetic) { - alpha = true - } - } - } - (words, alpha) -} - -fn strip_nick_prefix(s: &str) -> &str { - if s.is_empty() { - return s; - } - match s.as_bytes()[0] { - b'~' | b'&' | b'@' | b'%' | b'+' => &s[1..], - _ => s, - } -} - -pub fn freq(args: &ArgMatches) { - let env = Environment(args); - let (context, mut decoder, mut input, mut output) = (env.context(), - env.decoder(), - env.input(), - env.output()); - - let mut stats: HashMap<String, Person> = HashMap::new(); - - for e in decoder.decode(&context, &mut input) { - let m = match e { - Ok(m) => m, - Err(err) => error(Box::new(err)), - }; - - match m { - Event { ty: Type::Msg { ref from, ref content, .. }, .. } => { - let nick = strip_nick_prefix(from); - if stats.contains_key(nick) { - let p: &mut Person = stats.get_mut(nick).unwrap(); - let (words, alpha) = words_alpha(content); - p.lines += 1; - if alpha { - p.alpha_lines += 1 - } - p.words += words; - } else { - let (words, alpha) = words_alpha(content); - stats.insert(nick.to_owned(), - Person { - lines: 1, - alpha_lines: if alpha { 1 } else { 0 }, - words: words, - }); - } - } - _ => (), - } - } - - let mut stats: Vec<(String, Person)> = stats.into_iter().collect(); - stats.sort_by(|&(_, ref a), &(_, ref b)| b.words.cmp(&a.words)); - - let count = value_t!(args, "count", usize).unwrap_or(stats.len()); - for &(ref name, ref stat) in stats.iter().take(count) { - let _ = write!(&mut output, - "{}:\n\tTotal lines: {}\n\tLines without alphabetic characters: \ - {}\n\tTotal words: {}\n\tWords per line: {}\n", - name, - stat.lines, - stat.lines - stat.alpha_lines, - stat.words, - stat.words as f32 / stat.lines as f32); - } -} diff --git a/src/app/mod.rs b/src/app/mod.rs deleted file mode 100644 index 9b02b1e..0000000 --- a/src/app/mod.rs +++ /dev/null @@ -1,271 +0,0 @@ -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::{Path, PathBuf}; -use std::io::{self, BufRead, BufReader, BufWriter, Write}; -use std::fs::File; -use std::error::Error; -use std::ffi::OsStr; - -use ilc::context::Context; -use ilc::format::{self, Decode, Encode}; - -use chain; - -pub mod freq; - -pub fn error(e: Box<Error>) -> ! { - 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<Decode> { - 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<Encode> { - 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 { - let mut 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(), - }; - if args.is_present("infer_date") { - let input_files = gather_input(args); - match input_files.len() { - 0 => die("No input files given, can't infer date"), - 1 => { - if let Some(date) = input_files.get(0) - .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); - } - } - _n => die("Too many input files, can't infer date"), - } - } - context -} - -pub fn gather_input(args: &ArgMatches) -> Vec<PathBuf> { - 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() - } -} - -pub fn open_files(files: Vec<PathBuf>) -> Box<BufRead> { - if files.len() > 0 { - Box::new(BufReader::new(chain::Chain::new(files.iter() - .map(|p| File::open(p).unwrap()) - .collect()))) - } else { - Box::new(BufReader::new(io::stdin())) - } -} - -pub fn open_output(args: &ArgMatches) -> Box<Write> { - 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<BufRead> { - open_files(gather_input(self.0)) - } - pub fn output(&self) -> Box<Write> { - open_output(self.0) - } - pub fn decoder(&self) -> Box<Decode> { - force_decoder(self.0.value_of("format").or(self.0.value_of("input_format"))) - } - pub fn encoder(&self) -> Box<Encode> { - force_encoder(self.0.value_of("format").or(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 <nick> not present"); - - let mut last: Option<Event> = 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::Weechat; - 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<Event> = 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); - } - } - } - } -} diff --git a/src/chain.rs b/src/chain.rs deleted file mode 100644 index a8014b8..0000000 --- a/src/chain.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::io::{Read, Result, 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/context.rs b/src/context.rs deleted file mode 100644 index 4393457..0000000 --- a/src/context.rs +++ /dev/null @@ -1,9 +0,0 @@ - -use chrono::naive::date::NaiveDate; -use chrono::offset::fixed::FixedOffset; - -pub struct Context { - pub timezone: FixedOffset, - pub override_date: Option<NaiveDate>, - pub channel: Option<String>, -} diff --git a/src/event.rs b/src/event.rs deleted file mode 100644 index 0a6fb2b..0000000 --- a/src/event.rs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2015 Till Höppner -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Common structures to represent the actual log data in memory. -//! These will be used by all formats for encoding and decoding. - -use std::borrow::Cow; -use std::cmp::Ordering; -use std::hash::{Hash, Hasher}; - -use chrono::naive::time::NaiveTime; -use chrono::offset::fixed::FixedOffset; -use chrono::offset::local::Local; -use chrono::offset::TimeZone; - -/// A whole log, in memory. This structure does not specify its -/// use. It may represent a private query, or the log of a channel. -pub struct Log<'a> { - pub entries: Vec<Event<'a>>, -} - -/// Different log formats carry different amounts of information. Some might -/// hold enough information to calculate precise timestamps, others might -/// only suffice for the time of day. -#[derive(Clone, Debug, PartialEq, Eq, Ord, Hash, RustcEncodable, RustcDecodable)] -pub enum Time { - Unknown, - Hms(u8, u8, u8), - Timestamp(i64), -} - -impl Time { - pub fn from_format(tz: &FixedOffset, s: &str, f: &str) -> Time { - tz.datetime_from_str(s, f) - .map(|d| d.timestamp()) - .map(Time::Timestamp) - .unwrap_or(Time::Unknown) - } - - pub fn with_format(&self, tz: &FixedOffset, f: &str) -> String { - match self { - &Time::Unknown => panic!("Time data for this event is not present"), - &Time::Hms(h, m, s) => { - format!("{}", - NaiveTime::from_hms(h as u32, m as u32, s as u32).format(f)) - } - &Time::Timestamp(t) => format!("{}", tz.timestamp(t, 0).format(f)), - } - } - - pub fn as_timestamp(&self) -> i64 { - use self::Time::*; - match self { - &Unknown => 0, - &Hms(h, m, s) => { - Local::today() - .and_hms(h as u32, m as u32, s as u32) - .timestamp() - } - &Timestamp(i) => i, - } - } - - pub fn to_timestamp(&self) -> Time { - Time::Timestamp(self.as_timestamp()) - } -} - -impl PartialOrd for Time { - fn partial_cmp(&self, other: &Time) -> Option<Ordering> { - use self::Time::*; - match (self, other) { - (&Unknown, _) | (_, &Unknown) => None, - (&Hms(a_h, a_m, a_s), &Hms(b_h, b_m, b_s)) => { - if (a_h >= b_h && a_m >= b_m && a_s > b_s) || - (a_h >= b_h && a_m > b_m && a_s >= b_s) || - (a_h > b_h && a_m >= b_m && a_s >= b_s) { - Some(Ordering::Greater) - } else { - Some(Ordering::Less) - } - } - (&Timestamp(a), &Timestamp(b)) => Some(a.cmp(&b)), - _ => unimplemented!(), - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] -pub struct Event<'a> { - pub ty: Type<'a>, - pub time: Time, - pub channel: Option<Cow<'a, str>>, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] -pub struct User<'a> { - nicks: Cow<'a, str>, -} - -/// All representable events, such as messages, quits, joins -/// and topic changes. -#[derive(Clone, Debug, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)] -pub enum Type<'a> { - Connect, - Disconnect, - Msg { - from: Cow<'a, str>, - content: Cow<'a, str>, - }, - Action { - from: Cow<'a, str>, - content: Cow<'a, str>, - }, - Join { - nick: Cow<'a, str>, - mask: Option<Cow<'a, str>>, - }, - Part { - nick: Cow<'a, str>, - mask: Option<Cow<'a, str>>, - reason: Option<Cow<'a, str>>, - }, - Quit { - nick: Cow<'a, str>, - mask: Option<Cow<'a, str>>, - reason: Option<Cow<'a, str>>, - }, - Nick { - old_nick: Cow<'a, str>, - new_nick: Cow<'a, str>, - }, - Notice { - from: Cow<'a, str>, - content: Cow<'a, str>, - }, - Kick { - kicked_nick: Cow<'a, str>, - kicking_nick: Option<Cow<'a, str>>, - kick_message: Option<Cow<'a, str>>, - }, - Topic { - topic: Cow<'a, str>, - }, - TopicChange { - nick: Option<Cow<'a, str>>, - new_topic: Cow<'a, str>, - }, - Mode { - nick: Option<Cow<'a, str>>, - mode: Cow<'a, str>, - masks: Cow<'a, str>, - }, -} - -impl<'a> Type<'a> { - pub fn involves(&self, needle: &str) -> bool { - use self::Type::*; - match self { - &Msg { ref from, .. } => from == needle, - &Action { ref from, .. } => from == needle, - &Join { ref nick, .. } => nick == needle, - &Part { ref nick, .. } => nick == needle, - &Quit { ref nick, .. } => nick == 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, .. } => { - *kicked_nick == Cow::Borrowed(needle) || - kicking_nick.as_ref().map_or(false, |k| k.as_ref() == Cow::Borrowed(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, - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] -pub struct NoTimeHash<'a>(pub Event<'a>); - -impl<'a> Hash for NoTimeHash<'a> { - fn hash<H>(&self, state: &mut H) - where H: Hasher - { - self.0.ty.hash(state); - self.0.channel.hash(state); - } -} diff --git a/src/format/binary.rs b/src/format/binary.rs deleted file mode 100644 index 7cc4281..0000000 --- a/src/format/binary.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2015 Till Höppner -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::io::{BufRead, Write}; -use std::iter::Iterator; - -use event::Event; -use context::Context; -use format::{Decode, Encode}; - -use bincode::{self, SizeLimit}; - -pub struct Binary; - -pub struct Iter<'a> { - input: &'a mut BufRead, -} - -impl<'a> Iterator for Iter<'a> { - type Item = ::Result<Event<'a>>; - fn next(&mut self) -> Option<::Result<Event<'a>>> { - Some(bincode::rustc_serialize::decode_from::<_, Event>(&mut self.input, - SizeLimit::Infinite) - .map_err(|_| ::IlcError::BincodeDecode)) - } -} - -impl Encode for Binary { - fn encode<'a>(&'a self, - _context: &'a Context, - mut output: &'a mut Write, - event: &'a Event) - -> ::Result<()> { - bincode::rustc_serialize::encode_into(event, &mut output, SizeLimit::Infinite) - .map_err(|_| ::IlcError::BincodeEncode) - } -} - -impl Decode for Binary { - fn decode<'a>(&'a mut self, - _context: &'a Context, - input: &'a mut BufRead) - -> Box<Iterator<Item = ::Result<Event<'a>>> + 'a> { - Box::new(Iter { input: input }) - } -} diff --git a/src/format/energymech.rs b/src/format/energymech.rs deleted file mode 100644 index e8dded6..0000000 --- a/src/format/energymech.rs +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2015 Till Höppner -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::io::{BufRead, Write}; -use std::borrow::{Cow, ToOwned}; -use std::iter::Iterator; - -use event::{Event, Time, Type}; -use context::Context; -use format::{Decode, Encode, rejoin, strip_one}; - -use log::LogLevel::Info; - -use chrono::*; - -pub struct Energymech; - -static TIME_FORMAT: &'static str = "%H:%M:%S"; - -pub struct Iter<'a> { - context: &'a Context, - input: &'a mut BufRead, - buffer: Vec<u8>, -} - -impl<'a> Iterator for Iter<'a> { - type Item = ::Result<Event<'a>>; - fn next(&mut self) -> Option<::Result<Event<'a>>> { - fn parse_time(context: &Context, time: &str) -> Time { - let h = time[1..3].parse::<u32>().unwrap(); - let m = time[4..6].parse::<u32>().unwrap(); - let s = time[7..9].parse::<u32>().unwrap(); - if let Some(date) = context.override_date { - Time::Timestamp(context.timezone - .from_local_date(&date) - .and_time(NaiveTime::from_hms(h, m, s)) - .single() - .expect("Transformed log times can't be represented, due \ - to timezone transitions") - .timestamp()) - } else { - Time::Hms(h as u8, m as u8, s as u8) - } - } - - loop { - self.buffer.clear(); - match self.input.read_until(b'\n', &mut self.buffer) { - Ok(0) | Err(_) => return None, - Ok(_) => (), - } - - let buffer = String::from_utf8_lossy(&self.buffer); - - let mut split_tokens: Vec<char> = Vec::new(); - let tokens = buffer.split(|c: char| { - if c.is_whitespace() { - split_tokens.push(c); - true - } else { - false - } - }) - .collect::<Vec<_>>(); - - if log_enabled!(Info) { - info!("Original: `{}`", buffer); - info!("Parsing: {:?}", tokens); - } - - match &tokens[..tokens.len() - 1] { - [time, "*", nick, content..] => { - return Some(Ok(Event { - ty: Type::Action { - from: nick.to_owned().into(), - content: rejoin(content, &split_tokens[3..]), - }, - time: parse_time(&self.context, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - [time, "***", old, "is", "now", "known", "as", new] => { - return Some(Ok(Event { - ty: Type::Nick { - old_nick: old.to_owned().into(), - new_nick: new.to_owned().into(), - }, - time: parse_time(&self.context, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - [time, "***", nick, "sets", "mode:", mode, masks..] => { - return Some(Ok(Event { - ty: Type::Mode { - nick: Some(nick.to_owned().into()), - mode: mode.to_owned().into(), - masks: rejoin(&masks, &split_tokens[6..]).to_owned().into(), - }, - time: parse_time(&self.context, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - [time, "***", "Joins:", nick, host] => { - return Some(Ok(Event { - ty: Type::Join { - nick: nick.to_owned().into(), - mask: Some(strip_one(host).into()), - }, - time: parse_time(&self.context, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - [time, "***", "Parts:", nick, host, reason..] => { - return Some(Ok(Event { - ty: Type::Part { - nick: nick.to_owned().into(), - mask: Some(strip_one(host).into()), - reason: Some(strip_one(&rejoin(reason, &split_tokens[5..])).into()), - }, - time: parse_time(&self.context, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - [time, "***", "Quits:", nick, host, reason..] => { - return Some(Ok(Event { - ty: Type::Quit { - nick: nick.to_owned().into(), - mask: Some(strip_one(host).into()), - reason: Some(strip_one(&rejoin(reason, &split_tokens[5..])).into()), - }, - time: parse_time(&self.context, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - [time, "***", nick, "changes", "topic", "to", topic..] => { - return Some(Ok(Event { - ty: Type::TopicChange { - nick: Some(nick.to_owned().into()), - new_topic: strip_one(&rejoin(topic, &split_tokens[6..])).into(), - }, - time: parse_time(&self.context, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - [time, nick, content..] if nick.starts_with('<') && nick.ends_with('>') => { - return Some(Ok(Event { - ty: Type::Msg { - from: strip_one(nick).into(), - content: rejoin(content, &split_tokens[2..]), - }, - time: parse_time(&self.context, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - _ => (), - } - } - } -} - -impl Decode for Energymech { - fn decode<'a>(&'a mut self, - context: &'a Context, - input: &'a mut BufRead) - -> Box<Iterator<Item = ::Result<Event<'a>>> + 'a> { - Box::new(Iter { - context: context, - input: input, - buffer: Vec::new(), - }) - } -} - -impl Encode for Energymech { - fn encode<'a>(&'a self, - context: &'a Context, - mut output: &'a mut Write, - event: &'a Event) - -> ::Result<()> { - match event { - &Event { ty: Type::Msg { ref from, ref content }, ref time, .. } => { - try!(writeln!(&mut output, - "[{}] <{}> {}", - time.with_format(&context.timezone, TIME_FORMAT), - from, - content)) - } - &Event { ty: Type::Action { ref from, ref content }, ref time, .. } => { - try!(writeln!(&mut output, - "[{}] * {} {}", - time.with_format(&context.timezone, TIME_FORMAT), - from, - content)) - } - &Event { ty: Type::Nick { ref old_nick, ref new_nick }, ref time, .. } => { - try!(writeln!(&mut output, - "[{}] *** {} is now known as {}", - time.with_format(&context.timezone, TIME_FORMAT), - old_nick, - new_nick)) - } - &Event { ty: Type::Mode { ref nick, ref mode, ref masks }, ref time, .. } => { - try!(writeln!(&mut output, - "[{}] *** {} sets mode: {} {}", - time.with_format(&context.timezone, TIME_FORMAT), - nick.as_ref().expect("Nickname not present, but required."), - mode, - masks)) - } - &Event { ty: Type::Join { ref nick, ref mask }, ref time, .. } => { - try!(writeln!(&mut output, - "[{}] *** Joins: {} ({})", - time.with_format(&context.timezone, TIME_FORMAT), - nick, - mask.as_ref().expect("Mask not present, but required."))) - } - &Event { ty: Type::Part { ref nick, ref mask, ref reason }, ref time, .. } => { - try!(writeln!(&mut output, - "[{}] *** Parts: {} ({}) ({})", - time.with_format(&context.timezone, TIME_FORMAT), - nick, - mask.as_ref().expect("Mask not present, but required."), - reason.as_ref().unwrap_or(&Cow::Borrowed("")))) - } - &Event { ty: Type::Quit { ref nick, ref mask, ref reason }, ref time, .. } => { - try!(writeln!(&mut output, - "[{}] *** Quits: {} ({}) ({})", - time.with_format(&context.timezone, TIME_FORMAT), - nick, - mask.as_ref().expect("Mask not present, but required."), - reason.as_ref().expect("Reason not present, but required."))) - } - &Event { ty: Type::TopicChange { ref nick, ref new_topic }, ref time, .. } => { - try!(writeln!(&mut output, - "[{}] *** {} changes topic to '{}'", - time.with_format(&context.timezone, TIME_FORMAT), - nick.as_ref().expect("Nick not present, but required."), - new_topic)) - } - _ => (), - } - Ok(()) - } -} diff --git a/src/format/irssi.rs b/src/format/irssi.rs deleted file mode 100644 index 6afcd61..0000000 --- a/src/format/irssi.rs +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2015 Till Höppner -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::io::{ BufRead, Write }; -use std::borrow::{ ToOwned }; -use std::iter::{ Iterator }; - -use event::{ Event, Type, Time }; -use context::Context; -use format::{ Encode, Decode, rejoin, strip_one }; - -use l::LogLevel::Info; - -pub struct Irssi; - -static LOG_OPEN_FORMAT: &'static str = "%a %b %e %T %Y"; -static LINE_FORMAT: &'static str = "%H:%M"; - -pub struct Iter<'a> { - context: &'a Context, - input: &'a mut BufRead, - buffer: Vec<u8> -} - -impl<'a> Iterator for Iter<'a> { - type Item = ::Result<Event<'a>>; - fn next(&mut self) -> Option<::Result<Event<'a>>> { - fn parse_time(c: &Context, date: &str, time: &str) -> Time { - Time::from_format(&c.timezone, &format!("{} {}", date, time), TIME_DATE_FORMAT) - } - - loop { - self.buffer.clear(); - match self.input.read_until(b'\n', &mut self.buffer) { - Ok(0) | Err(_) => return None, - Ok(_) => () - } - - let buffer = String::from_utf8_lossy(&self.buffer); - - let mut split_tokens: Vec<char> = Vec::new(); - let tokens = buffer.split(|c: char| { - if c.is_whitespace() { split_tokens.push(c); true } else { false } - }).collect::<Vec<_>>(); - - if log_enabled!(Info) { - info!("Original: `{}`", buffer); - info!("Parsing: {:?}", tokens); - } - - match &tokens[..tokens.len() - 1] { - ["---", "Log", "opened", day_of_week, month, day, time, year] => { - year - }, - ["---", "Log", "closed", day_of_week, month, day, time, year] - => return Some(Ok(Event { - ty: Type::Disconnect, - time: parse_time(&self.context, date, time), - channel: self.context.channel.clone().map(Into::into) - })), - [time, "-!-", nick, host, "has", "joined", channel] - => return Some(Ok(Event { - ty: Type::Join { - nick: nick.to_owned().into(), - mask: Some(strip_one(host).into()), - }, - channel: Some(channel.to_owned().into()), - time: parse_time(&self.context, date, time) - })), - [time, "-!-", nick, host, "has", "left", channel, reason..] - => return Some(Ok(Event { - ty: Type::Part { - nick: nick.to_owned().into(), - mask: Some(strip_one(host).into()), - reason: Some(strip_one(&rejoin(reason, &split_tokens[8..])).into()), - }, - channel: Some(channel.to_owned().into()), - time: parse_time(&self.context, date, time) - })), - [time, "-!-", nick, host, "has", "quit", reason..] - => return Some(Ok(Event { - ty: Type::Quit { - nick: nick.to_owned().into(), - mask: Some(strip_one(host).into()), - reason: Some(strip_one(&rejoin(reason, &split_tokens[7..])).into()), - }, - time: parse_time(&self.context, date, time), - channel: self.context.channel.clone().map(Into::into) - })), - // TODO: reorder - [date, time, "--", notice, content..] - if notice.starts_with("Notice(") - => return Some(Ok(Event { - ty: Type::Notice { - from: notice["Notice(".len()..notice.len() - 2].to_owned().into(), - content: rejoin(content, &split_tokens[4..]), - }, - time: parse_time(&self.context, date, time), - channel: self.context.channel.clone().map(Into::into) - })), - [date, time, "--", nick, verb, "now", "known", "as", new_nick] - if verb == "is" || verb == "are" - => return Some(Ok(Event { - ty: Type::Nick { - old_nick: nick.to_owned().into(), - new_nick: new_nick.to_owned().into() - }, - time: parse_time(&self.context, date, time), - channel: self.context.channel.clone().map(Into::into) - })), - [date, time, sp, "*", nick, msg..] - if sp.clone().is_empty() - => return Some(Ok(Event { - ty: Type::Action { - from: nick.to_owned().into(), - content: rejoin(msg, &split_tokens[5..]), - }, - time: parse_time(&self.context, date, time), - channel: self.context.channel.clone().map(Into::into) - })), - [date, time, nick, msg..] - => return Some(Ok(Event { - ty: Type::Msg { - from: nick.to_owned().into(), - content: rejoin(msg, &split_tokens[3..]), - }, - time: parse_time(&self.context, date, time), - channel: self.context.channel.clone().map(Into::into) - })), - _ => () - } - } - } -} - -impl Decode for Irssi { - fn decode<'a>(&'a mut self, context: &'a Context, input: &'a mut BufRead) -> Box<Iterator<Item = ::Result<Event<'a>>> + 'a> { - Box::new(Iter { - context: context, - input: input, - buffer: Vec::new() - }) - } -} - -impl Encode for Irssi { - fn encode<'a>(&'a self, context: &'a Context, mut output: &'a mut Write, event: &'a Event) -> ::Result<()> { - match event { - &Event { ty: Type::Msg { ref from, ref content, .. }, ref time, .. } => { - try!(writeln!(&mut output, "{}\t{}\t{}", - time.with_format(&context.timezone, TIME_DATE_FORMAT), from, content)) - }, - &Event { ty: Type::Action { ref from, ref content, .. }, ref time, .. } => { - try!(writeln!(&mut output, "{}\t *\t{} {}", - time.with_format(&context.timezone, TIME_DATE_FORMAT), from, content)) - }, - &Event { ty: Type::Join { ref nick, ref mask, .. }, ref channel, ref time } => { - try!(writeln!(&mut output, "{}\t-->\t{} ({}) has joined {}", - time.with_format(&context.timezone, TIME_DATE_FORMAT), nick, - mask.as_ref().expect("Hostmask not present, but required."), - channel.as_ref().expect("Channel not present, but required."))) - }, - &Event { ty: Type::Part { ref nick, ref mask, ref reason }, ref channel, ref time } => { - try!(write!(&mut output, "{}\t<--\t{} ({}) has left {}", - time.with_format(&context.timezone, TIME_DATE_FORMAT), nick, - mask.as_ref().expect("Hostmask not present, but required."), - channel.as_ref().expect("Channel not present, but required."))); - if reason.is_some() && reason.as_ref().unwrap().len() > 0 { - try!(write!(&mut output, " ({})", reason.as_ref().unwrap())); - } - try!(write!(&mut output, "\n")) - }, - &Event { ty: Type::Quit { ref nick, ref mask, ref reason }, ref time, .. } => { - try!(write!(&mut output, "{}\t<--\t{} ({}) has quit", - time.with_format(&context.timezone, TIME_DATE_FORMAT), nick, - mask.as_ref().expect("Hostmask not present, but required."))); - if reason.is_some() && reason.as_ref().unwrap().len() > 0 { - try!(write!(&mut output, " ({})", reason.as_ref().unwrap())); - } - try!(write!(&mut output, "\n")) - }, - &Event { ty: Type::Disconnect, ref time, .. } => { - try!(writeln!(&mut output, "{}\t--\tirc: disconnected from server", - time.with_format(&context.timezone, TIME_DATE_FORMAT))) - }, - &Event { ty: Type::Notice { ref from, ref content }, ref time, .. } => { - try!(writeln!(&mut output, "{}\t--\tNotice({}): {}", - time.with_format(&context.timezone, TIME_DATE_FORMAT), from, content)) - }, - _ => () - } - Ok(()) - } -} diff --git a/src/format/mod.rs b/src/format/mod.rs deleted file mode 100644 index 8873db8..0000000 --- a/src/format/mod.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2015 Till Höppner -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Traits and structs for conversion between various formats. -//! As the source format may not provide the same information as the -//! target format, all formats must allow for omittable information. - -use std::iter; -use std::io::{BufRead, Write}; -use std::borrow::Cow; - -use event::Event; -use context::Context; - -pub use self::energymech::Energymech; -pub use self::weechat::Weechat; -pub use self::binary::Binary; -pub use self::msgpack::Msgpack; - -mod energymech; -mod weechat; -// pub mod irssi; -mod binary; -mod msgpack; - -pub trait Encode { - fn encode<'a>(&'a self, - context: &'a Context, - output: &'a mut Write, - event: &'a Event) - -> ::Result<()>; -} - -pub trait Decode { - fn decode<'a>(&'a mut self, - context: &'a Context, - input: &'a mut BufRead) - -> Box<Iterator<Item = ::Result<Event<'a>>> + 'a>; -} - -pub struct Dummy; - -impl Decode for Dummy { - fn decode<'a>(&'a mut self, - _context: &'a Context, - _input: &'a mut BufRead) - -> Box<Iterator<Item = ::Result<Event<'a>>> + 'a> { - Box::new(iter::empty()) - } -} - -pub fn decoder(format: &str) -> Option<Box<Decode>> { - match format { - "energymech" | "em" => Some(Box::new(Energymech)), - "weechat" | "w" => Some(Box::new(Weechat)), - // "irssi" => Some(Box::new(irssi::Irssi)), - "binary" => Some(Box::new(Binary)), - "msgpack" => Some(Box::new(Msgpack)), - _ => None, - } -} - -pub fn encoder(format: &str) -> Option<Box<Encode>> { - match format { - "energymech" | "em" => Some(Box::new(Energymech)), - "weechat" | "w" => Some(Box::new(Weechat)), - // "irssi" => Some(Box::new(irssi::Irssi)), - "binary" => Some(Box::new(Binary)), - "msgpack" => Some(Box::new(Msgpack)), - _ => None, - } -} - -fn rejoin(s: &[&str], splits: &[char]) -> Cow<'static, str> { - let len = s.iter().map(|s| s.len()).fold(0, |a, b| a + b); - let mut out = s.iter() - .zip(splits.iter()) - .fold(String::with_capacity(len), |mut s, (b, &split)| { - s.push_str(b); - s.push(split); - s - }); - out.pop(); - Cow::Owned(out) -} - -fn strip_one(s: &str) -> String { - if s.len() >= 2 { s[1..(s.len() - 1)].to_owned() } else { String::new() } -} diff --git a/src/format/msgpack.rs b/src/format/msgpack.rs deleted file mode 100644 index 36af1aa..0000000 --- a/src/format/msgpack.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2015 Till Höppner -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::io::{BufRead, Write}; -use std::iter::Iterator; - -use event::Event; -use context::Context; -use format::{Decode, Encode}; - -use rustc_serialize::{Decodable, Encodable}; -use msgpack::{Decoder, Encoder}; -use rmp::decode::ReadError; - -pub struct Msgpack; - -pub struct Iter<'a> { - input: &'a mut BufRead, -} - -impl<'a> Iterator for Iter<'a> { - type Item = ::Result<Event<'a>>; - fn next(&mut self) -> Option<::Result<Event<'a>>> { - use msgpack::decode; - match Event::decode(&mut Decoder::new(&mut self.input)) { - Ok(e) => Some(Ok(e)), - Err(decode::Error::InvalidMarkerRead(ReadError::UnexpectedEOF)) => None, - Err(e) => Some(Err(::IlcError::MsgpackDecode(e))), - } - } -} - -impl Encode for Msgpack { - fn encode<'a>(&'a self, - _context: &'a Context, - output: &'a mut Write, - event: &'a Event) - -> ::Result<()> { - event.encode(&mut Encoder::new(output)) - .map_err(|e| ::IlcError::MsgpackEncode(e)) - } -} - -impl Decode for Msgpack { - fn decode<'a>(&'a mut self, - _context: &'a Context, - input: &'a mut BufRead) - -> Box<Iterator<Item = ::Result<Event<'a>>> + 'a> { - Box::new(Iter { input: input }) - } -} diff --git a/src/format/weechat.rs b/src/format/weechat.rs deleted file mode 100644 index ccb0726..0000000 --- a/src/format/weechat.rs +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2015 Till Höppner -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::io::{BufRead, Write}; -use std::borrow::ToOwned; -use std::iter::Iterator; - -use event::{Event, Time, Type}; -use context::Context; -use format::{Decode, Encode, rejoin, strip_one}; - -use log::LogLevel::Info; - -pub struct Weechat; - -static TIME_DATE_FORMAT: &'static str = "%Y-%m-%d %H:%M:%S"; - -pub struct Iter<'a> { - context: &'a Context, - input: &'a mut BufRead, - buffer: Vec<u8>, -} - -impl<'a> Iterator for Iter<'a> { - type Item = ::Result<Event<'a>>; - fn next(&mut self) -> Option<::Result<Event<'a>>> { - fn parse_time(c: &Context, date: &str, time: &str) -> Time { - Time::from_format(&c.timezone, &format!("{} {}", date, time), TIME_DATE_FORMAT) - } - - loop { - self.buffer.clear(); - match self.input.read_until(b'\n', &mut self.buffer) { - Ok(0) | Err(_) => return None, - Ok(_) => (), - } - - let buffer = String::from_utf8_lossy(&self.buffer); - - let mut split_tokens: Vec<char> = Vec::new(); - let tokens = buffer.split(|c: char| { - if c.is_whitespace() { - split_tokens.push(c); - true - } else { - false - } - }) - .collect::<Vec<_>>(); - - if log_enabled!(Info) { - info!("Original: `{}`", buffer); - info!("Parsing: {:?}", tokens); - } - - match &tokens[..tokens.len() - 1] { - [date, time, "-->", nick, host, "has", "joined", channel, _..] => { - return Some(Ok(Event { - ty: Type::Join { - nick: nick.to_owned().into(), - mask: Some(strip_one(host).into()), - }, - channel: Some(channel.to_owned().into()), - time: parse_time(&self.context, date, time), - })) - } - [date, time, "<--", nick, host, "has", "left", channel, reason..] => { - return Some(Ok(Event { - ty: Type::Part { - nick: nick.to_owned().into(), - mask: Some(strip_one(host).into()), - reason: Some(strip_one(&rejoin(reason, &split_tokens[8..])).into()), - }, - channel: Some(channel.to_owned().into()), - time: parse_time(&self.context, date, time), - })) - } - [date, time, "<--", nick, host, "has", "quit", reason..] => { - return Some(Ok(Event { - ty: Type::Quit { - nick: nick.to_owned().into(), - mask: Some(strip_one(host).into()), - reason: Some(strip_one(&rejoin(reason, &split_tokens[7..])).into()), - }, - time: parse_time(&self.context, date, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - [date, time, "--", notice, content..] if notice.starts_with("Notice(") => { - return Some(Ok(Event { - ty: Type::Notice { - from: notice["Notice(".len()..notice.len() - 2].to_owned().into(), - content: rejoin(content, &split_tokens[4..]), - }, - time: parse_time(&self.context, date, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - [date, time, "--", "irc:", "disconnected", "from", "server", _..] => { - return Some(Ok(Event { - ty: Type::Disconnect, - time: parse_time(&self.context, date, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - [date, time, "--", nick, verb, "now", "known", "as", new_nick] if verb == "is" || - verb == "are" => { - return Some(Ok(Event { - ty: Type::Nick { - old_nick: nick.to_owned().into(), - new_nick: new_nick.to_owned().into(), - }, - time: parse_time(&self.context, date, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - [date, time, sp, "*", nick, msg..] if sp.clone().is_empty() => { - return Some(Ok(Event { - ty: Type::Action { - from: nick.to_owned().into(), - content: rejoin(msg, &split_tokens[5..]), - }, - time: parse_time(&self.context, date, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - [date, time, nick, msg..] => { - return Some(Ok(Event { - ty: Type::Msg { - from: nick.to_owned().into(), - content: rejoin(msg, &split_tokens[3..]), - }, - time: parse_time(&self.context, date, time), - channel: self.context.channel.clone().map(Into::into), - })) - } - _ => (), - } - } - } -} - -impl Decode for Weechat { - fn decode<'a>(&'a mut self, - context: &'a Context, - input: &'a mut BufRead) - -> Box<Iterator<Item = ::Result<Event<'a>>> + 'a> { - Box::new(Iter { - context: context, - input: input, - buffer: Vec::new(), - }) - } -} - -impl Encode for Weechat { - fn encode<'a>(&'a self, - context: &'a Context, - mut output: &'a mut Write, - event: &'a Event) - -> ::Result<()> { - match event { - &Event { ty: Type::Msg { ref from, ref content, .. }, ref time, .. } => { - try!(writeln!(&mut output, - "{}\t{}\t{}", - time.with_format(&context.timezone, TIME_DATE_FORMAT), - from, - content)) - } - &Event { ty: Type::Action { ref from, ref content, .. }, ref time, .. } => { - try!(writeln!(&mut output, - "{}\t *\t{} {}", - time.with_format(&context.timezone, TIME_DATE_FORMAT), - from, - content)) - } - &Event { ty: Type::Join { ref nick, ref mask, .. }, ref channel, ref time } => { - try!(writeln!(&mut output, - "{}\t-->\t{} ({}) has joined {}", - time.with_format(&context.timezone, TIME_DATE_FORMAT), - nick, - mask.as_ref().expect("Hostmask not present, but required."), - channel.as_ref().expect("Channel not present, but required."))) - } - &Event { ty: Type::Part { ref nick, ref mask, ref reason }, ref channel, ref time } => { - try!(write!(&mut output, - "{}\t<--\t{} ({}) has left {}", - time.with_format(&context.timezone, TIME_DATE_FORMAT), - nick, - mask.as_ref().expect("Hostmask not present, but required."), - channel.as_ref().expect("Channel not present, but required."))); - if reason.is_some() && reason.as_ref().unwrap().len() > 0 { - try!(write!(&mut output, " ({})", reason.as_ref().unwrap())); - } - try!(write!(&mut output, "\n")) - } - &Event { ty: Type::Quit { ref nick, ref mask, ref reason }, ref time, .. } => { - try!(write!(&mut output, - "{}\t<--\t{} ({}) has quit", - time.with_format(&context.timezone, TIME_DATE_FORMAT), - nick, - mask.as_ref().expect("Hostmask not present, but required."))); - if reason.is_some() && reason.as_ref().unwrap().len() > 0 { - try!(write!(&mut output, " ({})", reason.as_ref().unwrap())); - } - try!(write!(&mut output, "\n")) - } - &Event { ty: Type::Disconnect, ref time, .. } => { - try!(writeln!(&mut output, - "{}\t--\tirc: disconnected from server", - time.with_format(&context.timezone, TIME_DATE_FORMAT))) - } - &Event { ty: Type::Notice { ref from, ref content }, ref time, .. } => { - try!(writeln!(&mut output, - "{}\t--\tNotice({}): {}", - time.with_format(&context.timezone, TIME_DATE_FORMAT), - from, - content)) - } - _ => (), - } - Ok(()) - } -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 4772428..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2015 Till Höppner -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#![feature(slice_patterns)] -extern crate chrono; -#[macro_use] -extern crate log; -extern crate rustc_serialize; -extern crate bincode; -extern crate rmp; -extern crate rmp_serialize as msgpack; - -pub mod event; -pub mod format; -pub mod context; - -use std::convert::From; -use std::{io, result}; -use std::error::Error; -use std::fmt::{self, Display, Formatter}; - -use chrono::format::ParseError; - -pub type Result<T> = result::Result<T, IlcError>; - -#[derive(Debug)] -pub enum IlcError { - Parse(String), - Chrono(ParseError), - BincodeDecode, - BincodeEncode, - MsgpackEncode(msgpack::encode::Error), - MsgpackDecode(msgpack::decode::Error), - Io(io::Error), -} - -impl Display for IlcError { - fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { - fmt.write_str(self.description()) - } -} - -impl Error for IlcError { - fn description(&self) -> &str { - use IlcError::*; - match self { - &Parse(_) => "error while parsing", - &Chrono(_) => "error while parsing time strings", - &BincodeDecode => "error while decoding from binary", - &BincodeEncode => "error while encoding to binary", - &MsgpackDecode(_) => "error while decoding from msgpack", - &MsgpackEncode(_) => "error while encoding to msgpack", - &Io(_) => "error during input/output", - } - } - - fn cause(&self) -> Option<&Error> { - use IlcError::*; - match self { - &Parse(ref _e) => None, - &Chrono(ref e) => Some(e), - &BincodeDecode => None, - &BincodeEncode => None, - &MsgpackDecode(ref e) => Some(e), - &MsgpackEncode(ref e) => Some(e), - &Io(ref e) => Some(e), - } - } -} - -impl From<ParseError> for IlcError { - fn from(err: ParseError) -> IlcError { - IlcError::Chrono(err) - } -} - -impl From<io::Error> for IlcError { - fn from(err: io::Error) -> IlcError { - IlcError::Io(err) - } -} |