aboutsummaryrefslogtreecommitdiff
path: root/base
diff options
context:
space:
mode:
authorTill Höppner2016-02-25 06:48:03 +0100
committerTill Höppner2016-02-25 06:48:03 +0100
commit9f5dd9dad6b13476bab2c6eb3c6528f8ad49311a (patch)
tree1ca71876029cb466aa6230f1aead05b32f19bf6d /base
parent685aac1cc537692b2cf9342dcb6c26fa74c3c920 (diff)
downloadilc-9f5dd9dad6b13476bab2c6eb3c6528f8ad49311a.tar.gz
ilc-9f5dd9dad6b13476bab2c6eb3c6528f8ad49311a.tar.xz
ilc-9f5dd9dad6b13476bab2c6eb3c6528f8ad49311a.zip
Refactor... everything.
Diffstat (limited to 'base')
-rw-r--r--base/Cargo.toml9
-rw-r--r--base/src/context.rs9
-rw-r--r--base/src/error.rs54
-rw-r--r--base/src/event.rs188
-rw-r--r--base/src/format.rs18
-rw-r--r--base/src/lib.rs49
-rw-r--r--base/src/mod.rs47
7 files changed, 374 insertions, 0 deletions
diff --git a/base/Cargo.toml b/base/Cargo.toml
new file mode 100644
index 0000000..539990b
--- /dev/null
+++ b/base/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "ilc-base"
+version = "0.1.0"
+authors = ["Till Höppner <till@hoeppner.ws>"]
+
+[dependencies]
+log = "0.3.5"
+chrono = "0.2.19"
+rustc-serialize = "0.3.18"
diff --git a/base/src/context.rs b/base/src/context.rs
new file mode 100644
index 0000000..4393457
--- /dev/null
+++ b/base/src/context.rs
@@ -0,0 +1,9 @@
+
+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/base/src/error.rs b/base/src/error.rs
new file mode 100644
index 0000000..2806c78
--- /dev/null
+++ b/base/src/error.rs
@@ -0,0 +1,54 @@
+use std::{error, fmt, io, result};
+use std::error::Error as E;
+
+use chrono::format::ParseError;
+
+pub type Result<T> = result::Result<T, Error>;
+
+#[derive(Debug)]
+pub enum Error {
+ Parse(String),
+ Chrono(ParseError),
+ Io(io::Error),
+ Custom(Box<error::Error>),
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.write_str(self.description())
+ }
+}
+
+impl error::Error for Error {
+ fn description(&self) -> &str {
+ use self::Error::*;
+ match self {
+ &Parse(_) => "error while parsing",
+ &Chrono(_) => "error while parsing time strings",
+ &Io(_) => "error during input/output",
+ &Custom(ref e) => e.description(),
+ }
+ }
+
+ fn cause(&self) -> Option<&error::Error> {
+ use self::Error::*;
+ match self {
+ &Parse(ref _e) => None,
+ &Chrono(ref e) => Some(e),
+ &Io(ref e) => Some(e),
+ &Custom(ref e) => e.cause(),
+ }
+ }
+}
+
+impl From<ParseError> for Error {
+ fn from(err: ParseError) -> Error {
+ Error::Chrono(err)
+ }
+}
+
+impl From<io::Error> for Error {
+ fn from(err: io::Error) -> Error {
+ Error::Io(err)
+ }
+}
diff --git a/base/src/event.rs b/base/src/event.rs
new file mode 100644
index 0000000..e357800
--- /dev/null
+++ b/base/src/event.rs
@@ -0,0 +1,188 @@
+// 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 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,
+ }
+ }
+}
diff --git a/base/src/format.rs b/base/src/format.rs
new file mode 100644
index 0000000..46b2da6
--- /dev/null
+++ b/base/src/format.rs
@@ -0,0 +1,18 @@
+use std::borrow::Cow;
+
+pub 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)
+}
+
+pub fn strip_one(s: &str) -> String {
+ if s.len() >= 2 { s[1..(s.len() - 1)].to_owned() } else { String::new() }
+}
diff --git a/base/src/lib.rs b/base/src/lib.rs
new file mode 100644
index 0000000..9e96478
--- /dev/null
+++ b/base/src/lib.rs
@@ -0,0 +1,49 @@
+// 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;
+pub mod error;
+pub mod format;
+
+use std::io::{BufRead, Write};
+
+pub use context::Context;
+pub use event::Event;
+pub use error::*;
+
+pub trait Encode {
+ fn encode<'a>(&'a self,
+ context: &'a Context,
+ output: &'a mut Write,
+ event: &'a Event)
+ -> error::Result<()>;
+}
+
+pub trait Decode {
+ fn decode<'a>(&'a mut self,
+ context: &'a Context,
+ input: &'a mut BufRead)
+ -> Box<Iterator<Item = error::Result<Event<'a>>> + 'a>;
+}
diff --git a/base/src/mod.rs b/base/src/mod.rs
new file mode 100644
index 0000000..5374598
--- /dev/null
+++ b/base/src/mod.rs
@@ -0,0 +1,47 @@
+// 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 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())
+ }
+}