From edffba8b37e67722e450737a3723773f98dd923a Mon Sep 17 00:00:00 2001 From: Jokler Date: Tue, 13 Mar 2018 21:16:17 +0100 Subject: Add sed plugin --- src/plugins/mod.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/plugins/mod.rs') diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 9a3ba2f..6aed95e 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -1,6 +1,7 @@ //! Collection of plugins included pub mod help; pub mod url; +pub mod sed; pub mod emoji; pub mod tell; pub mod currency; -- cgit v1.2.3-70-g09d2 From 5c45046794e3c93f875d69193bb12d6608d45a8c Mon Sep 17 00:00:00 2001 From: Jokler Date: Sat, 7 Apr 2018 18:19:24 +0200 Subject: Add usefulness rating function to url titles --- src/lib.rs | 14 ++-- src/main.rs | 20 +++--- src/plugin.rs | 2 +- src/plugins/currency.rs | 2 +- src/plugins/emoji.rs | 2 +- src/plugins/factoids/database.rs | 10 +-- src/plugins/factoids/mod.rs | 24 ++++--- src/plugins/factoids/utils.rs | 7 +- src/plugins/help.rs | 2 +- src/plugins/keepnick.rs | 2 +- src/plugins/mod.rs | 10 +-- src/plugins/sed.rs | 10 +-- src/plugins/tell/database.rs | 8 +-- src/plugins/tell/mod.rs | 15 ++-- src/plugins/url.rs | 148 ++++++++++++++++++++++++++------------- src/utils.rs | 106 +++++++++++++++++++--------- 16 files changed, 238 insertions(+), 144 deletions(-) (limited to 'src/plugins/mod.rs') diff --git a/src/lib.rs b/src/lib.rs index 37f1225..67a99d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,6 +46,7 @@ extern crate lazy_static; #[macro_use] extern crate log; +extern crate antidote; extern crate chrono; extern crate circular_queue; extern crate humantime; @@ -53,22 +54,21 @@ extern crate irc; extern crate regex; extern crate reqwest; extern crate time; -extern crate antidote; +pub mod error; pub mod plugin; pub mod plugins; pub mod utils; -pub mod error; use std::collections::HashMap; use std::fmt; -use std::thread; use std::sync::Arc; +use std::thread; -pub use irc::client::prelude::*; -pub use irc::error::IrcError; use error::*; use failure::ResultExt; +pub use irc::client::prelude::*; +pub use irc::error::IrcError; use plugin::*; @@ -80,11 +80,11 @@ pub struct Bot<'a> { } impl<'a> Bot<'a> { - /// Creates a `Bot` without any plugins. + /// Creates a `Bot` without any plugins. /// By itself the bot only responds to a few simple CTCP commands /// defined per config file. /// Any other functionality has to be provided by plugins - /// which need to implement [`Plugin`](plugin/trait.Plugin.html). + /// which need to implement [`Plugin`](plugin/trait.Plugin.html). /// To send commands to a plugin /// the message has to start with the plugin's name prefixed by `cmd_prefix`. /// diff --git a/src/main.rs b/src/main.rs index e0b50eb..3660ccd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,25 +21,25 @@ extern crate failure; #[macro_use] extern crate log; +use log::{Level, LevelFilter, Metadata, Record}; +use std::collections::HashMap; #[cfg(feature = "mysql")] use std::sync::Arc; -use std::collections::HashMap; -use log::{Level, LevelFilter, Metadata, Record}; -use irc::client::reactor::IrcReactor; use glob::glob; +use irc::client::reactor::IrcReactor; +use frippy::plugins::currency::Currency; +use frippy::plugins::emoji::Emoji; +use frippy::plugins::factoids::Factoids; use frippy::plugins::help::Help; -use frippy::plugins::url::Url; +use frippy::plugins::keepnick::KeepNick; use frippy::plugins::sed::Sed; -use frippy::plugins::emoji::Emoji; use frippy::plugins::tell::Tell; -use frippy::plugins::currency::Currency; -use frippy::plugins::keepnick::KeepNick; -use frippy::plugins::factoids::Factoids; +use frippy::plugins::url::UrlTitles; -use frippy::Config; use failure::Error; +use frippy::Config; #[cfg(feature = "mysql")] embed_migrations!(); @@ -136,7 +136,7 @@ fn run() -> Result<(), Error> { let mut bot = frippy::Bot::new(&prefix); bot.add_plugin(Help::new()); - bot.add_plugin(Url::new(1024)); + bot.add_plugin(UrlTitles::new(1024)); bot.add_plugin(Sed::new(60)); bot.add_plugin(Emoji::new()); bot.add_plugin(Currency::new()); diff --git a/src/plugin.rs b/src/plugin.rs index 6c81741..653ec02 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,8 +1,8 @@ //! Definitions required for every `Plugin` use std::fmt; -use irc::client::prelude::*; use error::FrippyError; +use irc::client::prelude::*; /// Describes if a [`Plugin`](trait.Plugin.html) is done working on a /// [`Message`](../../irc/proto/message/struct.Message.html) or if another thread is required. diff --git a/src/plugins/currency.rs b/src/plugins/currency.rs index 99f46c8..e7a802d 100644 --- a/src/plugins/currency.rs +++ b/src/plugins/currency.rs @@ -13,8 +13,8 @@ use self::serde_json::Value; use plugin::*; -use error::FrippyError; use error::ErrorKind as FrippyErrorKind; +use error::FrippyError; use failure::ResultExt; #[derive(PluginName, Default, Debug)] diff --git a/src/plugins/emoji.rs b/src/plugins/emoji.rs index f1d9376..4ec7265 100644 --- a/src/plugins/emoji.rs +++ b/src/plugins/emoji.rs @@ -6,8 +6,8 @@ use irc::client::prelude::*; use plugin::*; -use error::FrippyError; use error::ErrorKind as FrippyErrorKind; +use error::FrippyError; use failure::Fail; use failure::ResultExt; diff --git a/src/plugins/factoids/database.rs b/src/plugins/factoids/database.rs index 7788d7c..321931f 100644 --- a/src/plugins/factoids/database.rs +++ b/src/plugins/factoids/database.rs @@ -1,20 +1,20 @@ #[cfg(feature = "mysql")] extern crate dotenv; +use std::collections::HashMap; #[cfg(feature = "mysql")] use std::sync::Arc; -use std::collections::HashMap; +#[cfg(feature = "mysql")] +use diesel::mysql::MysqlConnection; #[cfg(feature = "mysql")] use diesel::prelude::*; #[cfg(feature = "mysql")] -use diesel::mysql::MysqlConnection; +use failure::ResultExt; #[cfg(feature = "mysql")] use r2d2::Pool; #[cfg(feature = "mysql")] use r2d2_diesel::ConnectionManager; -#[cfg(feature = "mysql")] -use failure::ResultExt; use chrono::NaiveDateTime; @@ -124,8 +124,8 @@ impl Database for Arc>> { } fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError> { - use diesel; use self::factoids::columns; + use diesel; let conn = &*self.get().context(ErrorKind::NoConnection)?; match diesel::delete( diff --git a/src/plugins/factoids/mod.rs b/src/plugins/factoids/mod.rs index 10e512a..ba3ee8a 100644 --- a/src/plugins/factoids/mod.rs +++ b/src/plugins/factoids/mod.rs @@ -1,13 +1,13 @@ extern crate rlua; -use std::fmt; -use std::str::FromStr; use self::rlua::prelude::*; -use irc::client::prelude::*; use antidote::RwLock; +use irc::client::prelude::*; +use std::fmt; +use std::str::FromStr; -use time; use chrono::NaiveDateTime; +use time; use plugin::*; pub mod database; @@ -15,11 +15,12 @@ use self::database::Database; mod utils; use self::utils::*; +use utils::Url; -use failure::ResultExt; +use self::error::*; use error::ErrorKind as FrippyErrorKind; use error::FrippyError; -use self::error::*; +use failure::ResultExt; static LUA_SANDBOX: &'static str = include_str!("sandbox.lua"); @@ -52,7 +53,8 @@ impl Factoids { created: NaiveDateTime::from_timestamp(tm.sec, 0u32), }; - Ok(self.factoids.write() + Ok(self.factoids + .write() .insert_factoid(&factoid) .map(|()| "Successfully added!")?) } @@ -75,7 +77,10 @@ impl Factoids { let name = command.tokens.remove(0); let url = &command.tokens[0]; - let content = ::utils::download(url, Some(1024)).context(ErrorKind::Download)?; + let content = Url::from(url.as_ref()) + .max_kib(1024) + .request() + .context(ErrorKind::Download)?; Ok(self.create_factoid(&name, &content, &command.source)?) } @@ -118,7 +123,8 @@ impl Factoids { } }; - let factoid = self.factoids.read() + let factoid = self.factoids + .read() .get_factoid(name, idx) .context(ErrorKind::NotFound)?; diff --git a/src/plugins/factoids/utils.rs b/src/plugins/factoids/utils.rs index 70ac8a7..fd08da1 100644 --- a/src/plugins/factoids/utils.rs +++ b/src/plugins/factoids/utils.rs @@ -3,17 +3,18 @@ extern crate reqwest; use std::thread; use std::time::Duration; -use utils; use super::rlua::prelude::*; +use utils::Url; use self::LuaError::RuntimeError; pub fn download(_: &Lua, url: String) -> Result { - match utils::download(&url, Some(1024)) { + let url = Url::from(url).max_kib(1024); + match url.request() { Ok(v) => Ok(v), Err(e) => Err(RuntimeError(format!( "Failed to download {} - {}", - url, + url.as_str(), e.to_string() ))), } diff --git a/src/plugins/help.rs b/src/plugins/help.rs index 7e3658d..9eb152a 100644 --- a/src/plugins/help.rs +++ b/src/plugins/help.rs @@ -2,8 +2,8 @@ use irc::client::prelude::*; use plugin::*; -use error::FrippyError; use error::ErrorKind as FrippyErrorKind; +use error::FrippyError; use failure::ResultExt; #[derive(PluginName, Default, Debug)] diff --git a/src/plugins/keepnick.rs b/src/plugins/keepnick.rs index 58ac167..aa2e485 100644 --- a/src/plugins/keepnick.rs +++ b/src/plugins/keepnick.rs @@ -2,8 +2,8 @@ use irc::client::prelude::*; use plugin::*; -use error::FrippyError; use error::ErrorKind as FrippyErrorKind; +use error::FrippyError; use failure::ResultExt; #[derive(PluginName, Default, Debug)] diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 6aed95e..a8fc818 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -1,9 +1,9 @@ //! Collection of plugins included -pub mod help; -pub mod url; -pub mod sed; -pub mod emoji; -pub mod tell; pub mod currency; +pub mod emoji; pub mod factoids; +pub mod help; pub mod keepnick; +pub mod sed; +pub mod tell; +pub mod url; diff --git a/src/plugins/sed.rs b/src/plugins/sed.rs index 8ccb2f7..f766809 100644 --- a/src/plugins/sed.rs +++ b/src/plugins/sed.rs @@ -1,17 +1,17 @@ -use std::collections::HashMap; +use antidote::RwLock; use circular_queue::CircularQueue; use regex::{Regex, RegexBuilder}; -use antidote::RwLock; +use std::collections::HashMap; use irc::client::prelude::*; use plugin::*; -use failure::Fail; -use failure::ResultExt; +use self::error::*; use error::ErrorKind as FrippyErrorKind; use error::FrippyError; -use self::error::*; +use failure::Fail; +use failure::ResultExt; lazy_static! { static ref RE: Regex = Regex::new(r"^s/((?:\\/|[^/])+)/((?:\\/|[^/])*)/(?:(\w+))?\s*$").unwrap(); diff --git a/src/plugins/tell/database.rs b/src/plugins/tell/database.rs index 42c0d88..522df5a 100644 --- a/src/plugins/tell/database.rs +++ b/src/plugins/tell/database.rs @@ -1,15 +1,15 @@ #[cfg(feature = "mysql")] extern crate dotenv; +use std::collections::HashMap; #[cfg(feature = "mysql")] use std::sync::Arc; -use std::collections::HashMap; -#[cfg(feature = "mysql")] -use diesel::prelude::*; #[cfg(feature = "mysql")] use diesel::mysql::MysqlConnection; #[cfg(feature = "mysql")] +use diesel::prelude::*; +#[cfg(feature = "mysql")] use r2d2::Pool; #[cfg(feature = "mysql")] use r2d2_diesel::ConnectionManager; @@ -138,8 +138,8 @@ impl Database for Arc>> { } fn delete_tells(&mut self, receiver: &str) -> Result<(), TellError> { - use diesel; use self::tells::columns; + use diesel; let conn = &*self.get().context(ErrorKind::NoConnection)?; diesel::delete(tells::table.filter(columns::receiver.eq(receiver))) diff --git a/src/plugins/tell/mod.rs b/src/plugins/tell/mod.rs index 42032be..c681d43 100644 --- a/src/plugins/tell/mod.rs +++ b/src/plugins/tell/mod.rs @@ -1,18 +1,18 @@ -use irc::client::prelude::*; use antidote::RwLock; +use irc::client::prelude::*; -use time; -use std::time::Duration; use chrono::NaiveDateTime; use humantime::format_duration; +use std::time::Duration; +use time; use plugin::*; -use failure::Fail; -use failure::ResultExt; +use self::error::*; use error::ErrorKind as FrippyErrorKind; use error::FrippyError; -use self::error::*; +use failure::Fail; +use failure::ResultExt; pub mod database; use self::database::Database; @@ -98,7 +98,8 @@ impl Tell { } fn on_namelist(&self, client: &IrcClient, channel: &str) -> Result<(), FrippyError> { - let receivers = self.tells.read() + let receivers = self.tells + .read() .get_receivers() .context(FrippyErrorKind::Tell)?; diff --git a/src/plugins/url.rs b/src/plugins/url.rs index ec98900..aba5b0d 100644 --- a/src/plugins/url.rs +++ b/src/plugins/url.rs @@ -5,46 +5,49 @@ use irc::client::prelude::*; use regex::Regex; use plugin::*; -use utils; +use utils::Url; use self::error::*; -use error::FrippyError; use error::ErrorKind as FrippyErrorKind; +use error::FrippyError; use failure::Fail; use failure::ResultExt; lazy_static! { - static ref RE: Regex = Regex::new(r"(^|\s)(https?://\S+)").unwrap(); + static ref URL_RE: Regex = Regex::new(r"(^|\s)(https?://\S+)").unwrap(); + static ref WORD_RE: Regex = Regex::new(r"(\w+)").unwrap(); } #[derive(PluginName, Debug)] -pub struct Url { +pub struct UrlTitles { max_kib: usize, } -impl Url { - /// If a file is larger than `max_kib` KiB the download is stopped - pub fn new(max_kib: usize) -> Url { - Url { max_kib: max_kib } - } - - fn grep_url(&self, msg: &str) -> Option { - let captures = RE.captures(msg)?; - debug!("Url captures: {:?}", captures); +#[derive(Clone, Debug)] +struct Title(String); - Some(captures.get(2)?.as_str().to_owned()) +impl From for Title { + fn from(title: String) -> Self { + Title(title) } +} +impl From for String { + fn from(title: Title) -> Self { + title.0 + } +} - fn get_ogtitle<'a>(&self, body: &str) -> Result<String, UrlError> { - let title = body.find("property=\"og:title\"") +impl Title { + fn find_by_delimiters(body: &str, delimiters: [&str; 3]) -> Result<Self, UrlError> { + let title = body.find(delimiters[0]) .map(|tag| { body[tag..] - .find("content=\"") - .map(|offset| tag + offset + 9) + .find(delimiters[1]) + .map(|offset| tag + offset + delimiters[1].len()) .map(|start| { body[start..] - .find("\"") + .find(delimiters[2]) .map(|offset| start + offset) .map(|end| &body[start..end]) }) @@ -52,53 +55,96 @@ impl Url { .and_then(|s| s.and_then(|s| s)) .ok_or(ErrorKind::MissingTitle)?; - debug!("Title: {:?}", title); + debug!("delimiters: {:?}", delimiters); + debug!("title: {:?}", title); - htmlescape::decode_html(title).map_err(|_| ErrorKind::HtmlDecoding.into()) + htmlescape::decode_html(title) + .map(|t| t.into()) + .map_err(|_| ErrorKind::HtmlDecoding.into()) } - fn get_title<'a>(&self, body: &str) -> Result<String, UrlError> { - let title = body.find("<title") - .map(|tag| { - body[tag..] - .find('>') - .map(|offset| tag + offset + 1) - .map(|start| { - body[start..] - .find("") - .map(|offset| start + offset) - .map(|end| &body[start..end]) - }) - }) - .and_then(|s| s.and_then(|s| s)) - .ok_or(ErrorKind::MissingTitle)?; + fn find_ogtitle<'a>(body: &str) -> Result { + Self::find_by_delimiters(body, ["property=\"og:title\"", "content=\"", "\""]) + } - debug!("Title: {:?}", title); + fn find_title<'a>(body: &str) -> Result { + Self::find_by_delimiters(body, ["", ""]) + } - htmlescape::decode_html(title).map_err(|_| ErrorKind::HtmlDecoding.into()) + // TODO Improve logic + fn is_useful(&self, url: &str) -> bool { + for word in WORD_RE.find_iter(&self.0) { + let w = word.as_str().to_lowercase(); + if w.len() > 2 && !url.to_lowercase().contains(&w) { + return true; + } + } + + return false; + } + + fn into_useful_title<'a>(self, url: &str) -> Result { + if self.is_useful(url) { + Ok(self) + } else { + Err(ErrorKind::UselessTitle)? + } + } + + fn clean_up(self) -> Self { + self.0.trim().replace('\n', "|").replace('\r', "|").into() + } + + pub fn find_useful_ogtitle<'a>(body: &str, url: &str) -> Result { + Self::find_ogtitle(body) + .and_then(|t| t.into_useful_title(url)) + .map(|t| t.clean_up()) + } + + pub fn find_useful_title<'a>(body: &str, url: &str) -> Result { + Self::find_title(body) + .and_then(|t| t.into_useful_title(url)) + .map(|t| t.clean_up()) + } +} + +impl UrlTitles { + /// If a file is larger than `max_kib` KiB the download is stopped + pub fn new(max_kib: usize) -> Self { + UrlTitles { max_kib: max_kib } + } + + fn grep_url<'a>(&self, msg: &'a str) -> Option> { + let captures = URL_RE.captures(msg)?; + debug!("Url captures: {:?}", captures); + + Some(captures.get(2)?.as_str().into()) } fn url(&self, text: &str) -> Result { - let url = self.grep_url(text).ok_or(ErrorKind::MissingUrl)?; - let body = utils::download(&url, Some(self.max_kib)).context(ErrorKind::Download)?; + let url = self.grep_url(text) + .ok_or(ErrorKind::MissingUrl)? + .max_kib(self.max_kib); + let body = url.request().context(ErrorKind::Download)?; - let title = match self.get_ogtitle(&body) { + let title = match Title::find_useful_ogtitle(&body, url.as_str()) { Ok(t) => t, - Err(e) => if e.kind() == ErrorKind::MissingTitle { - self.get_title(&body)? - } else { - Err(e)? - } + Err(e) => match e.kind() { + ErrorKind::MissingTitle | ErrorKind::UselessTitle => { + Title::find_useful_title(&body, url.as_str())? + } + _ => Err(e)?, + }, }; - Ok(title.trim().replace('\n', "|").replace('\r', "|")) + Ok(title.into()) } } -impl Plugin for Url { +impl Plugin for UrlTitles { fn execute(&self, _: &IrcClient, message: &Message) -> ExecutionStatus { match message.command { - Command::PRIVMSG(_, ref msg) => if RE.is_match(msg) { + Command::PRIVMSG(_, ref msg) => if URL_RE.is_match(msg) { ExecutionStatus::RequiresThread } else { ExecutionStatus::Done @@ -151,6 +197,10 @@ pub mod error { #[fail(display = "No title was found")] MissingTitle, + /// Useless title error + #[fail(display = "Title was not helpful")] + UselessTitle, + /// Html decoding error #[fail(display = "Failed to decode Html characters")] HtmlDecoding, diff --git a/src/utils.rs b/src/utils.rs index b6c4cf4..6614095 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,49 +1,85 @@ -use std::str; +use std::borrow::Cow; use std::io::{self, Read}; use reqwest::Client; use reqwest::header::Connection; -use failure::ResultExt; use self::error::{DownloadError, ErrorKind}; +use failure::ResultExt; + +#[derive(Clone, Debug)] +pub struct Url<'a> { + url: Cow<'a, str>, + max_kib: Option, +} + +impl<'a> From for Url<'a> { + fn from(url: String) -> Self { + Url { + url: Cow::from(url), + max_kib: None, + } + } +} + +impl<'a> From<&'a str> for Url<'a> { + fn from(url: &'a str) -> Self { + Url { + url: Cow::from(url), + max_kib: None, + } + } +} + +impl<'a> Url<'a> { + pub fn max_kib(mut self, limit: usize) -> Self { + self.max_kib = Some(limit); + self + } + + /// Downloads the file and converts it to a String. + /// Any invalid bytes are converted to a replacement character. + /// + /// The error indicated either a failed download or + /// that the limit set by max_kib() was reached. + pub fn request(&self) -> Result { + let mut response = Client::new() + .get(self.url.as_ref()) + .header(Connection::close()) + .send() + .context(ErrorKind::Connection)?; -/// Downloads the file and converts it to a String. -/// Any invalid bytes are converted to a replacement character. -/// -/// The error indicated either a failed download or that the DownloadLimit was reached -pub fn download(url: &str, max_kib: Option) -> Result { - let mut response = Client::new() - .get(url) - .header(Connection::close()) - .send() - .context(ErrorKind::Connection)?; - - // 100 kibibyte buffer - let mut buf = [0; 100 * 1024]; - let mut written = 0; - let mut bytes = Vec::new(); - - // Read until we reach EOF or max_kib KiB - loop { - let len = match response.read(&mut buf) { - Ok(0) => break, - Ok(len) => len, - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue, - Err(e) => Err(e).context(ErrorKind::Read)?, - }; - - bytes.extend_from_slice(&buf[..len]); - written += len; - - // Check if the file is too large to download - if let Some(max_kib) = max_kib { - if written > max_kib * 1024 { - Err(ErrorKind::DownloadLimit)?; + // 100 kibibyte buffer + let mut buf = [0; 100 * 1024]; + let mut written = 0; + let mut bytes = Vec::new(); + + // Read until we reach EOF or max_kib KiB + loop { + let len = match response.read(&mut buf) { + Ok(0) => break, + Ok(len) => len, + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue, + Err(e) => Err(e).context(ErrorKind::Read)?, + }; + + bytes.extend_from_slice(&buf[..len]); + written += len; + + // Check if the file is too large to download + if let Some(max_kib) = self.max_kib { + if written > max_kib * 1024 { + Err(ErrorKind::DownloadLimit)?; + } } } + + Ok(String::from_utf8_lossy(&bytes).into_owned()) } - Ok(String::from_utf8_lossy(&bytes).into_owned()) + pub fn as_str(&self) -> &str { + &self.url + } } pub mod error { -- cgit v1.2.3-70-g09d2 From 90bbc48e8f65566f38ee9bd0b10fbc53ce7ac4a7 Mon Sep 17 00:00:00 2001 From: Jokler Date: Sat, 12 May 2018 21:40:54 +0200 Subject: Remind: Add initial remind plugin Mysql is not supported yet. --- Cargo.lock | 611 +++++++++++++++++++++++------------------ src/error.rs | 4 + src/main.rs | 6 + src/plugins/help.rs | 9 +- src/plugins/mod.rs | 1 + src/plugins/remind/database.rs | 128 +++++++++ src/plugins/remind/mod.rs | 290 +++++++++++++++++++ src/plugins/remind/parser.rs | 250 +++++++++++++++++ 8 files changed, 1030 insertions(+), 269 deletions(-) create mode 100644 src/plugins/remind/database.rs create mode 100644 src/plugins/remind/mod.rs create mode 100644 src/plugins/remind/parser.rs (limited to 'src/plugins/mod.rs') diff --git a/Cargo.lock b/Cargo.lock index 16016e0..427197f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,14 @@ dependencies = [ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "antidote" version = "1.0.0" @@ -26,13 +34,13 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -41,16 +49,16 @@ name = "backtrace-sys" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "base64" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -61,7 +69,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bitflags" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -71,41 +79,53 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "build_const" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "byteorder" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bytes" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "cargo_metadata" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cc" -version = "1.0.9" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "chrono" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -115,31 +135,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "clippy" -version = "0.0.191" +version = "0.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "clippy_lints 0.0.191 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy_lints 0.0.198 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "clippy_lints" -version = "0.0.191" +version = "0.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "cargo_metadata 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -162,10 +185,10 @@ dependencies = [ [[package]] name = "crc" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -175,11 +198,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "crossbeam-deque" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -188,27 +211,19 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam-utils" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crossbeam-utils" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -222,49 +237,52 @@ dependencies = [ [[package]] name = "derive-error-chain" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.9 (registry+https://github.com/rust-lang/crates.io-index)", + "syntex_fmt_macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "diesel" -version = "1.1.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "diesel_derives 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel_derives 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "mysqlclient-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "diesel_derives" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "diesel_infer_schema" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "infer_schema_macros 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "infer_schema_macros 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "diesel_migrations" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "migrations_internals 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "migrations_macros 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "migrations_internals 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "migrations_macros 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -274,7 +292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "derive-error-chain 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -282,9 +300,9 @@ name = "dotenv" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "derive-error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "derive-error-chain 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -359,7 +377,7 @@ name = "encoding_rs" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -372,7 +390,7 @@ name = "error-chain" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -380,7 +398,7 @@ name = "failure" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -426,12 +444,12 @@ name = "frippy" version = "0.4.0" dependencies = [ "antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "circular-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.191 (registry+https://github.com/rust-lang/crates.io-index)", - "diesel 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "diesel_infer_schema 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "diesel_migrations 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.198 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel_infer_schema 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel_migrations 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "dotenv 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "frippy_derive 0.1.0", @@ -444,12 +462,12 @@ dependencies = [ "log4rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2-diesel 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", "rlua 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "unicode_names 0.1.7 (git+https://github.com/Jokler/unicode_names?branch=update-to-latest-unicode)", ] @@ -467,7 +485,7 @@ name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -478,7 +496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -486,7 +504,7 @@ name = "futures-cpupool" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -525,22 +543,23 @@ dependencies = [ [[package]] name = "hyper" -version = "0.11.24" +version = "0.11.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -552,10 +571,10 @@ name = "hyper-tls" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -568,7 +587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -578,19 +597,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "infer_schema_internals" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "diesel 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "infer_schema_macros" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dotenv 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "infer_schema_internals 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "infer_schema_internals 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -610,21 +629,21 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-mockstream 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -685,8 +704,8 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -707,8 +726,8 @@ name = "log" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -722,7 +741,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -730,11 +749,11 @@ dependencies = [ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "log-mdc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", "serde-value 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_yaml 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_yaml 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -759,25 +778,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "migrations_internals" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "diesel 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "migrations_macros" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "migrations_internals 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "migrations_internals 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "mime" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -788,9 +807,9 @@ name = "mime_guess" version = "2.0.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -799,7 +818,7 @@ name = "miniz-sys" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -837,8 +856,8 @@ name = "mysqlclient-sys" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -849,7 +868,7 @@ dependencies = [ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -860,7 +879,7 @@ name = "net2" version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -872,10 +891,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num-integer" -version = "0.1.36" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -883,12 +902,12 @@ name = "num-traits" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -908,18 +927,18 @@ dependencies = [ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.27 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "openssl-sys" -version = "0.9.27" +version = "0.9.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -938,33 +957,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "phf" -version = "0.7.21" +version = "0.7.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "phf_codegen" -version = "0.7.21" +version = "0.7.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", - "phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "phf_generator" -version = "0.7.21" +version = "0.7.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "phf_shared" -version = "0.7.21" +version = "0.7.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -973,7 +992,7 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -984,6 +1003,14 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proc-macro2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pulldown-cmark" version = "0.1.2" @@ -1016,6 +1043,14 @@ dependencies = [ "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quote" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "r2d2" version = "0.8.2" @@ -1031,7 +1066,7 @@ name = "r2d2-diesel" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "diesel 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1062,19 +1097,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "regex" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.5.3" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1085,12 +1120,12 @@ name = "relay" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "remove_dir_all" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1101,19 +1136,19 @@ name = "reqwest" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "libflate 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1132,9 +1167,17 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rustc_version" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "safemem" version = "0.2.0" @@ -1142,7 +1185,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "schannel" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1159,7 +1202,7 @@ dependencies = [ [[package]] name = "scoped-tls" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1193,6 +1236,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1202,7 +1246,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.36" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1211,38 +1255,27 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ordered-float 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.36" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive_internals 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_derive_internals" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.13" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1252,18 +1285,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_yaml" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1307,6 +1340,16 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syn" +version = "0.13.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "synom" version = "0.11.3" @@ -1324,6 +1367,14 @@ dependencies = [ "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syntex_fmt_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "take" version = "0.1.0" @@ -1335,7 +1386,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1349,7 +1400,7 @@ dependencies = [ [[package]] name = "time" -version = "0.1.39" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1359,43 +1410,55 @@ dependencies = [ [[package]] name = "tokio" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-threadpool 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-core" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-executor" -version = "0.1.1" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-fs" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1403,8 +1466,8 @@ name = "tokio-io" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1413,7 +1476,7 @@ name = "tokio-mockstream" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1422,14 +1485,14 @@ name = "tokio-proto" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1439,11 +1502,11 @@ name = "tokio-reactor" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1452,7 +1515,7 @@ name = "tokio-service" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1460,8 +1523,8 @@ name = "tokio-tcp" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1470,15 +1533,15 @@ dependencies = [ [[package]] name = "tokio-threadpool" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-deque 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-executor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1486,18 +1549,27 @@ name = "tokio-timer" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tokio-timer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tokio-tls" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-core 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1506,8 +1578,8 @@ name = "tokio-udp" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1516,10 +1588,10 @@ dependencies = [ [[package]] name = "toml" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1566,7 +1638,7 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1633,7 +1705,7 @@ dependencies = [ [[package]] name = "vcpkg" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1695,37 +1767,38 @@ dependencies = [ [metadata] "checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45" "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" -"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2" +"checksum backtrace 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea58cd16fd6c9d120b5bcb01d63883ae4cc7ba2aed35c1841b862a3c7ef6639" "checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" -"checksum base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "229d032f1a99302697f10b27167ae6d03d49d032e6a8e2550e8d3fc13356d2b4" +"checksum base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9263aa6a38da271eec5c91a83ce1e800f093c8535788d403d626d8d5c3f8f007" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" -"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" +"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" "checksum bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f382711e76b9de6c744cc00d0497baba02fb00a787f088c879f01d09468e32" -"checksum build_const 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90dc84f5e62d2ebe7676b83c22d33b6db8bd27340fb6ffbff0a364efa0cb9c9" -"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" -"checksum bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7db437d718977f6dc9b2e3fd6fc343c02ac6b899b73fdd2179163447bd9ce9" -"checksum cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "2b4911e4bdcb4100c7680e7e854ff38e23f1b34d4d9e079efae3da2801341ffc" -"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" -"checksum chrono 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ba5f60682a4c264e7f8d77b82e7788938a76befdf949d4a98026d19099c9d873" +"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" +"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" +"checksum bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1d50c876fb7545f5f289cd8b2aee3f359d073ae819eed5d6373638e2c61e59" +"checksum cargo_metadata 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6ebd6272a2ca4fd39dbabbd6611eb03df45c2259b3b80b39a9ff8fbdcf42a4b3" +"checksum cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebb87d1116151416c0cf66a0e3fb6430cccd120fd6300794b4dfaa050ac40ba" +"checksum cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "405216fd8fe65f718daa7102ea808a946b6ce40c742998fbfd3463645552de18" +"checksum chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6" "checksum circular-queue 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be63960d0419728d75f18d0132e5da310d2db6ef31db808a733a8566afe5545" -"checksum clippy 0.0.191 (registry+https://github.com/rust-lang/crates.io-index)" = "9e9e82aa6f8e55fc7110bda2c333c671c31b36da976237ecbde9958dea89e08b" -"checksum clippy_lints 0.0.191 (registry+https://github.com/rust-lang/crates.io-index)" = "7fd834b607d54d7eb70df08115d6c434a775fe5a12f3cc59d2ac331f09f04c15" +"checksum clippy 0.0.198 (registry+https://github.com/rust-lang/crates.io-index)" = "da3a62431bbcebe5250a1235e022cc61bcc2f32405d8dc08da4011d223c6a4ba" +"checksum clippy_lints 0.0.198 (registry+https://github.com/rust-lang/crates.io-index)" = "9517a4eee5daa6eaf318a5bd7a4db0bcd5d92e8d8f22c3e341e60cf1746c73a4" "checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" "checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" -"checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7" +"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" "checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19" -"checksum crossbeam-deque 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c1bdc73742c36f7f35ebcda81dbb33a7e0d33757d03a06d9ddca762712ec5ea2" +"checksum crossbeam-deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe8153ef04a7594ded05b427ffad46ddeaf22e63fd48d42b3e1e3bb4db07cae7" "checksum crossbeam-epoch 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4e2817eb773f770dcb294127c011e22771899c21d18fce7dd739c0b9832e81" -"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b" "checksum derive-error-chain 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9ca9ade651388daad7c993f005d0d20c4f6fe78c1cdc93e95f161c6f5ede4a" -"checksum derive-error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92183014af72c63aea490e66526c712bf1066ac50f66c9f34824f02483ec1d98" -"checksum diesel 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925325c57038f2f14c0413bdf6a92ca72acff644959d0a1a9ebf8d19be7e9c01" -"checksum diesel_derives 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "28e2b2605ac6a3b9a586383f5f8b2b5f1108f07a421ade965b266289d2805e79" -"checksum diesel_infer_schema 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd41decf55679a8486a3ea5e5de20e8f2a48c76d57177cd05f37f4d166f48647" -"checksum diesel_migrations 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0928a7d6f27c849954185416bd59439837de55fbc89e2985b0e46e756ae4e3da" +"checksum derive-error-chain 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cb4450afbe280461e78299b39182a085b70e3e71be049cf4a588ad72f1e44d33" +"checksum diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24815a0c2094f2c8dafe74ab3b9e975892f44acbb94b4d4b4898025a7615efa4" +"checksum diesel_derives 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6471a2b637b414d3ee1504cf230409a550381c79204282f8fe06c527e4ae56be" +"checksum diesel_infer_schema 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7ca3b65de2209668e7de12cb0b771e39bd37fbd652aa560d9843b730dbc17bd9" +"checksum diesel_migrations 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "992026fb46e684eaede3d9d05737de34bdcdcb7f97b389b3c0fa7999389d504e" "checksum dotenv 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d6f0e2bb24d163428d8031d3ebd2d2bd903ad933205a97d0f18c7c1aade380f3" "checksum dotenv 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a70de3c590ce18df70743cace1cf12565637a0b26fd8b04ef10c7d33fdc66cdc" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" @@ -1748,7 +1821,7 @@ dependencies = [ "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5a3176836efa0b37f0e321b86672dfada1564aeb516fbed67b7c24050a0263" +"checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" "checksum getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "b900c08c1939860ce8b54dc6a89e26e00c04c380fd0e09796799bd7f12861e05" @@ -1756,12 +1829,12 @@ dependencies = [ "checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" "checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37" "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" -"checksum hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)" = "df4dd5dae401458087396b6db7fabc4d6760aa456a5fa8e92bda549f39cae661" +"checksum hyper 0.11.26 (registry+https://github.com/rust-lang/crates.io-index)" = "66b16eb6213713f3c72d0ed14ce56423ae84dced8df73d2a2c8675f0495ae7ea" "checksum hyper-tls 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a5aa51f6ae9842239b0fac14af5f22123b8432b4cc774a44ff059fcba0f675ca" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" "checksum if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "61bb90bdd39e3af69b0172dfc6130f6cd6332bf040fbb9bdd4401d37adbd48b8" -"checksum infer_schema_internals 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8269258ae59744e7d5bdc44144c4b2319a355ff2ba34a3f629ae8888508a67c7" -"checksum infer_schema_macros 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fcc56d0b579656088758b435c63ad286be3ec5de53f18b2bbe161e85fb3fd0b" +"checksum infer_schema_internals 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e29654a5ade7e34a51637f40b45fa128c7fd62b0d4c3e2a19efca7caf7d9abf6" +"checksum infer_schema_macros 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bf0bd7c4960bcef19d34ff32de05c84c914a5eebcb80b9329dd55de764cda4fe" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum irc 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a30eb0fe918d4aa89c28e19240dd0935ec2c1a667036dc3d41f0ee3ccc599288" "checksum itertools 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)" = "f58856976b776fedd95533137617a02fb25719f40e7d9b01c7043cd65474f450" @@ -1782,9 +1855,9 @@ dependencies = [ "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum migrations_internals 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd916de6df9ac7e811e7e1ac28e0abfebe5205f3b29a7bda9ec8a41ee980a4eb" -"checksum migrations_macros 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a550cfd76f6cfdf15a7b541893d7c79b68277b0b309f12179211a373a56e617" -"checksum mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e00e17be181010a91dbfefb01660b17311059dc8c7f48b9017677721e732bd" +"checksum migrations_internals 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "54b8f3128f95b1e5b5f5d5d7ce2156b306b9d11dd50e23b594f25247e30ca83a" +"checksum migrations_macros 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a616c2538e1f09bf402efae6feb73c2711bb137c794b797f3fb8b3df84275d4b" +"checksum mime 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0b28683d0b09bbc20be1c9b3f6f24854efb1356ffcffee08ea3f6e65596e85fa" "checksum mime_guess 2.0.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130ea3c9c1b65dba905ab5a4d9ac59234a9585c24d135f264e187fe7336febbd" "checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4" "checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe" @@ -1793,86 +1866,92 @@ dependencies = [ "checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0" "checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" -"checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" +"checksum num-integer 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac0ea58d64a89d9d6b7688031b3be9358d6c919badcf7fbb0527ccfd891ee45" "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -"checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364" +"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985" -"checksum openssl-sys 0.9.27 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdc5c4a02e69ce65046f1763a0181107038e02176233acb0b3351d7cc588f9" +"checksum openssl-sys 0.9.30 (registry+https://github.com/rust-lang/crates.io-index)" = "73ae718c3562989cd3a0a5c26610feca02f8116822f6f195e6cf4887481e57f5" "checksum ordered-float 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "58d25b6c0e47b20d05226d288ff434940296e7e2f8b877975da32f862152241f" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc" -"checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f" -"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03" -"checksum phf_shared 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "07e24b0ca9643bdecd0632f2b3da6b1b89bbb0030e0b992afc1113b23a7bc2f2" -"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" +"checksum phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7d37a244c75a9748e049225155f56dbcb98fe71b192fd25fd23cb914b5ad62f2" +"checksum phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4048fe7dd7a06b8127ecd6d3803149126e9b33c7558879846da3a63f734f2b" +"checksum phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "05a079dd052e7b674d21cb31cbb6c05efd56a2cd2827db7692e2f1a507ebd998" +"checksum phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2261d544c2bb6aa3b10022b0be371b9c7c64f762ef28c6f5d4f1ef6d97b5930" +"checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f" "checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" +"checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" "checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" "checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408" +"checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" "checksum r2d2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f9078ca6a8a5568ed142083bb2f7dc9295b69d16f867ddcc9849e51b17d8db46" "checksum r2d2-diesel 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9c29bad92da76d02bc2c020452ebc3a3fe6fa74cfab91e711c43116e4fb1a3" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" -"checksum regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "aec3f58d903a7d2a9dc2bf0e41a746f4530e0cab6b615494e058f67a3ef947fb" -"checksum regex-syntax 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b2550876c31dc914696a6c2e01cbce8afba79a93c8ae979d2fe051c0230b3756" +"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" +"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" -"checksum remove_dir_all 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dfc5b3ce5d5ea144bb04ebd093a9e14e9765bcfec866aecda9b6dec43b3d1e24" +"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "241faa9a8ca28a03cbbb9815a5d085f271d4c0168a19181f106aa93240c22ddb" "checksum rlua 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4d6a9d2d1da31dd5cb4878789b924e46a600bdca4895b30f2efd6370d0dfc80e" -"checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" +"checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649" +"checksum rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa04a10c68c1c4eacb4337fd883b435997ede17a9385784b990777686b09a" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" -"checksum schannel 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "fbaffce35eb61c5b00846e73128b0cd62717e7c0ec46abbec132370d013975b4" +"checksum schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "85fd9df495640643ad2d00443b3d78aae69802ad488debab4f1dd52fc1806ade" "checksum scheduled-thread-pool 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a2ff3fc5223829be817806c6441279c676e454cc7da608faf03b0ccc09d3889" -"checksum scoped-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8674d439c964889e2476f474a3bf198cc9e199e77499960893bac5de7e9218a4" +"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum security-framework 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "dfa44ee9c54ce5eecc9de7d5acbad112ee58755239381f687e564004ba4a2332" "checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)" = "c70142ae874a42c70e03c63c6a49abe2ea0079b090bf6e136e99252fc1974bd6" +"checksum serde 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "97f6a6c3caba0cf8f883b53331791036404ce3c1bd895961cf8bb2f8cecfd84b" "checksum serde-value 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "52903ade2290cbd61a0937a66a268f26cebf246e3ddd7964a8babb297111fb0d" -"checksum serde_derive 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)" = "6fffe22d41dbddcead5b2c380c4714d44f2eb39292f7e7a0d966d2d45bf56408" -"checksum serde_derive_internals 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d2f04ed291686ce195a5c8f554aaf36e50a721fbf829ee3b6151e6f85eccf945" -"checksum serde_json 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "5c508584d9913df116b91505eec55610a2f5b16e9ed793c46e4d0152872b3e74" +"checksum serde_derive 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "f51b0ef935cf8a41a77bce553da1f8751a739b7ad82dd73669475a22e6ecedb0" +"checksum serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f3ad6d546e765177cf3dded3c2e424a8040f870083a0e64064746b958ece9cb1" "checksum serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce0fd303af908732989354c6f02e05e2e6d597152870f2c6990efb0577137480" -"checksum serde_yaml 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e0f868d400d9d13d00988da49f7f02aeac6ef00f11901a8c535bd59d777b9e19" +"checksum serde_yaml 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "107bb818146aaf922e7bbcf6a940f1db2f0dcf381779b451e400331b2c6f86db" "checksum siphasher 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0df90a788073e8d0235a67e50441d47db7c8ad9debd91cbf43736a2a92d36537" "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" "checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" "checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97c05b8ebc34ddd6b967994d5c6e9852fa92f8b82b3858c39451f97346dcce5" +"checksum syn 0.13.9 (registry+https://github.com/rust-lang/crates.io-index)" = "505550dded6ff93eb63bd9d0ada380ffccd9f51c046a5e80a3078d53fcef0038" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" +"checksum syntex_fmt_macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5386bdc48758d136af85b3880548e1f3a9fad8d7dc2b38bdb48c36a9cdefc0" "checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" -"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" -"checksum tokio 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "65bd27f59c223e7c9e406bcdadb453e143bcf1242e162ae6c0f0eb6d14487306" -"checksum tokio-core 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "799492ccba3d8ed5e41f2520a7cfd504cb65bbfe5fbbbd0012e335ae5f188051" -"checksum tokio-executor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3aca092a94dc6e736819347a990a86ed734a6543a9d6f817929fa4dc8c4334e2" +"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" +"checksum tokio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d00555353b013e170ed8bc4e13f648a317d1fd12157dbcae13f7013f6cf29f5" +"checksum tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "aeeffbbb94209023feaef3c196a41cbcdafa06b4a6f893f68779bb5e53796f71" +"checksum tokio-executor 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8cac2a7883ff3567e9d66bb09100d09b33d90311feca0206c7ca034bc0c55113" +"checksum tokio-fs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "76766830bbf9a2d5bfb50c95350d56a2e79e2c80f675967fff448bc615899708" "checksum tokio-io 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af9eb326f64b2d6b68438e1953341e00ab3cf54de7e35d92bfc73af8555313a" "checksum tokio-mockstream 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "41bfc436ef8b7f60c19adf3df086330ae9992385e4d8c53b17a323cad288e155" "checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" "checksum tokio-reactor 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3cedc8e5af5131dc3423ffa4f877cce78ad25259a9a62de0613735a13ebc64b" "checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" "checksum tokio-tcp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec9b094851aadd2caf83ba3ad8e8c4ce65a42104f7b94d9e6550023f0407853f" -"checksum tokio-threadpool 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2057ff8a75d33639f9ea1b4b85cb113c7bbf4e06d132f148521d12cb6daa1a22" +"checksum tokio-threadpool 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5783254b10c7c84a56f62c74766ef7e5b83d1f13053218c7cab8d3f2c826fa0e" "checksum tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc" +"checksum tokio-timer 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535fed0ccee189f3d48447587697ba3fd234b3dbbb091f0ec4613ddfec0a7c4c" "checksum tokio-tls 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "772f4b04e560117fe3b0a53e490c16ddc8ba6ec437015d91fa385564996ed913" "checksum tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "137bda266504893ac4774e0ec4c2108f7ccdbcb7ac8dced6305fe9e4e0b5041a" -"checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e" +"checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" "checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" "checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" +"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode_names 0.1.7 (git+https://github.com/Jokler/unicode_names?branch=update-to-latest-unicode)" = "" @@ -1882,7 +1961,7 @@ dependencies = [ "checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22" -"checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b" +"checksum vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ed0f6789c8a85ca41bbc1c9d175422116a9869bd1cf31bb08e1493ecce60380" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" diff --git a/src/error.rs b/src/error.rs index ce56a04..dc34eb7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -32,4 +32,8 @@ pub enum ErrorKind { /// A Factoids error #[fail(display = "A Factoids error has occured")] Factoids, + + /// A Remind error + #[fail(display = "A Remind error has occured")] + Remind, } diff --git a/src/main.rs b/src/main.rs index aae6035..3f56c50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,6 +34,7 @@ use frippy::plugins::emoji::Emoji; use frippy::plugins::factoids::Factoids; use frippy::plugins::help::Help; use frippy::plugins::keepnick::KeepNick; +use frippy::plugins::remind::Remind; use frippy::plugins::sed::Sed; use frippy::plugins::tell::Tell; use frippy::plugins::url::UrlTitles; @@ -125,11 +126,14 @@ fn run() -> Result<(), Error> { let pool = Arc::new(pool); bot.add_plugin(Factoids::new(pool.clone())); bot.add_plugin(Tell::new(pool.clone())); + // TODO Use mysql pool + bot.add_plugin(Remind::new(HashMap::new())); info!("Connected to MySQL server") } Err(e) => { bot.add_plugin(Factoids::new(HashMap::new())); bot.add_plugin(Tell::new(HashMap::new())); + bot.add_plugin(Remind::new(HashMap::new())); error!("Failed to run migrations: {}", e); } }, @@ -138,6 +142,7 @@ fn run() -> Result<(), Error> { } else { bot.add_plugin(Factoids::new(HashMap::new())); bot.add_plugin(Tell::new(HashMap::new())); + bot.add_plugin(Remind::new(HashMap::new())); } } #[cfg(not(feature = "mysql"))] @@ -147,6 +152,7 @@ fn run() -> Result<(), Error> { } bot.add_plugin(Factoids::new(HashMap::new())); bot.add_plugin(Tell::new(HashMap::new())); + bot.add_plugin(Remind::new(HashMap::new())); } if let Some(disabled_plugins) = disabled_plugins { diff --git a/src/plugins/help.rs b/src/plugins/help.rs index a02931c..b75086f 100644 --- a/src/plugins/help.rs +++ b/src/plugins/help.rs @@ -26,9 +26,12 @@ impl Plugin for Help { fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> { Ok(client - .send_notice(&command.source, "Available commands: help, currency, tell, factoids\r\n\ - For more detailed help call help on the specific command.\r\n\ - Example: 'currency help'") + .send_notice( + &command.source, + "Available commands: help, currency, tell, factoids, remind\r\n\ + For more detailed help call help on the specific command.\r\n\ + Example: 'currency help'", + ) .context(FrippyErrorKind::Connection)?) } diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index a8fc818..99e35db 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -4,6 +4,7 @@ pub mod emoji; pub mod factoids; pub mod help; pub mod keepnick; +pub mod remind; pub mod sed; pub mod tell; pub mod url; diff --git a/src/plugins/remind/database.rs b/src/plugins/remind/database.rs new file mode 100644 index 0000000..c0c127e --- /dev/null +++ b/src/plugins/remind/database.rs @@ -0,0 +1,128 @@ +#[cfg(feature = "mysql")] +extern crate dotenv; + +use std::collections::HashMap; +use std::collections::hash_map::Entry; +use std::fmt; + +use chrono::NaiveDateTime; + +use super::error::*; + +#[derive(Clone, Debug)] +pub struct Event { + pub id: i64, + pub receiver: String, + pub content: String, + pub author: String, + pub time: NaiveDateTime, + pub repeat: Option, +} + +impl fmt::Display for Event { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}: {} reminds {} to \"{}\" at {}", + self.id, self.author, self.receiver, self.content, self.time + ) + } +} + +#[derive(Debug)] +pub struct NewEvent<'a> { + pub receiver: &'a str, + pub content: &'a str, + pub author: &'a str, + pub time: &'a NaiveDateTime, + pub repeat: Option, +} + +pub trait Database: Send + Sync { + fn insert_event(&mut self, event: &NewEvent) -> Result<(), RemindError>; + fn update_event_time(&mut self, id: i64, &NaiveDateTime) -> Result<(), RemindError>; + fn get_events_before(&self, time: &NaiveDateTime) -> Result, RemindError>; + fn get_user_events(&self, user: &str) -> Result, RemindError>; + fn get_event(&self, id: i64) -> Result; + fn delete_event(&mut self, id: i64) -> Result<(), RemindError>; +} + +// HashMap +impl Database for HashMap { + fn insert_event(&mut self, event: &NewEvent) -> Result<(), RemindError> { + let mut id = 0; + while self.contains_key(&id) { + id += 1; + } + + let event = Event { + id: id, + receiver: event.receiver.to_owned(), + content: event.content.to_owned(), + author: event.author.to_owned(), + time: event.time.clone(), + repeat: event.repeat, + }; + + match self.insert(id, event) { + None => Ok(()), + Some(_) => Err(ErrorKind::Duplicate)?, + } + } + + fn update_event_time(&mut self, id: i64, time: &NaiveDateTime) -> Result<(), RemindError> { + let entry = self.entry(id); + + match entry { + Entry::Occupied(mut v) => v.get_mut().time = *time, + Entry::Vacant(_) => return Err(ErrorKind::NotFound.into()), + } + + Ok(()) + } + + fn get_events_before(&self, time: &NaiveDateTime) -> Result, RemindError> { + let mut events = Vec::new(); + + for (_, event) in self.iter() { + if &event.time < time { + events.push(event.clone()) + } + } + + if events.is_empty() { + Err(ErrorKind::NotFound.into()) + } else { + Ok(events) + } + } + + fn get_user_events(&self, user: &str) -> Result, RemindError> { + let mut events = Vec::new(); + + for (_, event) in self.iter() { + if event.receiver.eq_ignore_ascii_case(user) { + events.push(event.clone()) + } + } + + if events.is_empty() { + Err(ErrorKind::NotFound.into()) + } else { + Ok(events) + } + } + + fn get_event(&self, id: i64) -> Result { + Ok(self.get(&id) + .map(|ev| ev.clone()) + .ok_or(ErrorKind::NotFound)?) + } + + fn delete_event(&mut self, id: i64) -> Result<(), RemindError> { + match self.remove(&id) { + Some(_) => Ok(()), + None => Err(ErrorKind::NotFound)?, + } + } +} diff --git a/src/plugins/remind/mod.rs b/src/plugins/remind/mod.rs new file mode 100644 index 0000000..0893589 --- /dev/null +++ b/src/plugins/remind/mod.rs @@ -0,0 +1,290 @@ +use antidote::RwLock; +use irc::client::prelude::*; +use std::thread::{sleep, spawn}; +use std::{fmt, sync::Arc, time::Duration}; + +use chrono::{self, NaiveDateTime}; +use time; + +use plugin::*; +pub mod database; +mod parser; +use self::database::Database; +use self::parser::CommandParser; + +use self::error::*; +use error::ErrorKind as FrippyErrorKind; +use error::FrippyError; +use failure::ResultExt; + +fn get_time() -> NaiveDateTime { + let tm = time::now().to_timespec(); + NaiveDateTime::from_timestamp(tm.sec, 0u32) +} + +fn get_events(db: &RwLock, in_next: chrono::Duration) -> Vec { + loop { + let before = get_time() + in_next; + match db.read().get_events_before(&before) { + Ok(events) => return events, + Err(e) => { + if e.kind() != ErrorKind::NotFound { + error!("Failed to get events: {}", e); + } + } + } + + debug!("Sleeping for {:?}", in_next); + sleep(in_next.to_std().expect("Failed to convert look ahead time")); + } +} + +fn run(client: &IrcClient, db: Arc>) { + let look_ahead = chrono::Duration::minutes(2); + + let mut events = get_events(&db, look_ahead); + + let mut sleep_time = look_ahead + .to_std() + .expect("Failed to convert look ahead time"); + + loop { + let now = get_time(); + for event in events { + if event.time <= now { + let msg = format!("Reminder from {}: {}", event.author, event.content); + if let Err(e) = client.send_notice(&event.receiver, &msg) { + error!("Failed to send reminder: {}", e); + } else { + debug!("Sent reminder {:?}", event); + + if let Some(repeat) = event.repeat { + let next_time = event.time + chrono::Duration::seconds(repeat as i64); + + if let Err(e) = db.write().update_event_time(event.id, &next_time) { + error!("Failed to update reminder: {}", e); + } else { + debug!("Updated time on: {:?}", event); + } + } else if let Err(e) = db.write().delete_event(event.id) { + error!("Failed to delete reminder: {}", e); + } + } + } else { + let until_event = (event.time - now) + .to_std() + .expect("Failed to convert until event time"); + + if until_event < sleep_time { + sleep_time = until_event + Duration::from_secs(1); + } + } + } + + debug!("Sleeping for {:?}", sleep_time); + sleep(sleep_time); + sleep_time = Duration::from_secs(120); + + events = get_events(&db, look_ahead); + } +} + +#[derive(PluginName)] +pub struct Remind { + events: Arc>, + has_reminder: RwLock, +} + +impl Remind { + pub fn new(db: T) -> Self { + let events = Arc::new(RwLock::new(db)); + + Remind { + events: events, + has_reminder: RwLock::new(false), + } + } + + fn set(&self, command: PluginCommand) -> Result<&str, RemindError> { + let parser = CommandParser::try_from_tokens(command.tokens)?; + debug!("parser: {:?}", parser); + + let mut target = parser.get_target(); + if target == "me" { + target = &command.source; + } + + let event = database::NewEvent { + receiver: target, + content: &parser.get_message(), + author: &command.source, + time: &parser.get_time(Duration::from_secs(120))?, + repeat: parser + .get_repeat(Duration::from_secs(300))? + .map(|d| d.as_secs()), + }; + + debug!("New event: {:?}", event); + + Ok(self.events.write().insert_event(&event).map(|()| "Got it")?) + } + + fn list(&self, user: &str) -> Result { + let mut events = self.events.read().get_user_events(user)?; + + let mut list = events.remove(0).to_string(); + for ev in events { + list.push_str("\r\n"); + list.push_str(&ev.to_string()); + } + + Ok(list) + } + + fn delete(&self, mut command: PluginCommand) -> Result<&str, RemindError> { + let id = command + .tokens + .remove(0) + .parse::() + .context(ErrorKind::Parsing)?; + let event = self.events.read().get_event(id)?; + + if event.receiver.eq_ignore_ascii_case(&command.source) + || event.author.eq_ignore_ascii_case(&command.source) + { + self.events + .write() + .delete_event(id) + .map(|()| "Successfully deleted") + } else { + Ok("Only the author or receiver can delete a reminder") + } + } + + fn help(&self) -> &str { + "usage: remind \r\n\ + subcommands: new, list, delete, help" + } +} + +impl Plugin for Remind { + fn execute(&self, client: &IrcClient, _: &Message) -> ExecutionStatus { + let mut has_reminder = self.has_reminder.write(); + if !*has_reminder { + let events = Arc::clone(&self.events); + let client = client.clone(); + + spawn(move || run(&client, events)); + + *has_reminder = true; + } + + ExecutionStatus::Done + } + + fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> { + panic!("Remind should not use frippy's threading") + } + + fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), FrippyError> { + if command.tokens.is_empty() { + return Ok(client + .send_notice(&command.source, &ErrorKind::InvalidCommand.to_string()) + .context(FrippyErrorKind::Connection)?); + } + + let source = command.source.clone(); + + let sub_command = command.tokens.remove(0); + let response = match sub_command.as_ref() { + "new" => self.set(command).map(|s| s.to_owned()), + "delete" => self.delete(command).map(|s| s.to_owned()), + "list" => self.list(&source), + "help" => Ok(self.help().to_owned()), + _ => Err(ErrorKind::InvalidCommand.into()), + }; + + let result = match response { + Ok(msg) => client + .send_notice(&source, &msg) + .context(FrippyErrorKind::Connection)?, + Err(e) => { + let message = e.to_string(); + + client + .send_notice(&source, &message) + .context(FrippyErrorKind::Connection)?; + + Err(e).context(FrippyErrorKind::Remind)? + } + }; + + Ok(result) + } + + fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result { + Err(String::from( + "Evaluation of commands is not implemented for remind at this time", + )) + } +} + +impl fmt::Debug for Remind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Remind {{ ... }}") + } +} + +pub mod error { + #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)] + #[error = "RemindError"] + pub enum ErrorKind { + /// Invalid command error + #[fail(display = "Incorrect Command. Send \"currency help\" for help.")] + InvalidCommand, + + /// Missing message error + #[fail(display = "Reminder needs to have a description")] + MissingMessage, + + /// Missing receiver error + #[fail(display = "Specify who to remind")] + MissingReceiver, + + /// Missing time error + #[fail(display = "Reminder needs to have a time")] + MissingTime, + + /// Invalid time error + #[fail(display = "Could not parse time")] + InvalidTime, + + /// Invalid date error + #[fail(display = "Could not parse date")] + InvalidDate, + + /// Parse error + #[fail(display = "Could not parse integers")] + Parsing, + + /// Ambigous time error + #[fail(display = "Time specified is ambiguous")] + AmbiguousTime, + + /// Time too short error + #[fail(display = "Reminder needs to be in over 2 minutes")] + TimeShort, + + /// Repeat time too short error + #[fail(display = "Repeat time needs to be over 5 minutes")] + RepeatTimeShort, + + /// Duplicate error + #[fail(display = "Entry already exists")] + Duplicate, + + /// Not found error + #[fail(display = "No events found")] + NotFound, + } +} diff --git a/src/plugins/remind/parser.rs b/src/plugins/remind/parser.rs new file mode 100644 index 0000000..2dbb040 --- /dev/null +++ b/src/plugins/remind/parser.rs @@ -0,0 +1,250 @@ +use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; +use humantime::parse_duration; +use std::time::Duration; +use time; + +use super::error::*; +use failure::ResultExt; + +#[derive(Default, Debug)] +pub struct CommandParser { + on_date: Option, + at_time: Option, + in_duration: Option, + every_time: Option, + target: String, + message: Option, +} + +#[derive(PartialEq, Clone, Copy)] +enum ParseState { + None, + On, + At, + In, + Every, + Msg, +} + +impl CommandParser { + pub fn try_from_tokens(tokens: Vec) -> Result { + if tokens.is_empty() { + return Err(ErrorKind::MissingReceiver.into()); + } + + let mut parser = CommandParser::default(); + let mut state = ParseState::None; + + let mut iter = tokens.into_iter(); + parser.target = iter.next() + .expect("This should be guaranteed by the length check"); + + let mut cur_str = String::new(); + while let Some(token) = iter.next() { + let next_state = match token.as_ref() { + "on" => ParseState::On, + "at" => ParseState::At, + "in" => ParseState::In, + "every" => ParseState::Every, + "to" => ParseState::Msg, + _ => { + if !cur_str.is_empty() { + cur_str.push(' '); + } + cur_str.push_str(&token); + state + } + }; + + if next_state != state { + if state != ParseState::None { + parser = parser.add_string_by_state(&state, cur_str)?; + cur_str = String::new(); + } + + state = next_state; + } + } + parser = parser.add_string_by_state(&state, cur_str)?; + + if parser.message.is_none() { + return Err(ErrorKind::MissingMessage.into()); + } + + if parser.in_duration.is_some() && parser.at_time.is_some() + || parser.in_duration.is_some() && parser.on_date.is_some() + { + return Err(ErrorKind::AmbiguousTime.into()); + } + + if parser.in_duration.is_none() && parser.at_time.is_none() && parser.on_date.is_none() { + return Err(ErrorKind::MissingTime.into()); + } + + Ok(parser) + } + + fn add_string_by_state(self, state: &ParseState, string: String) -> Result { + use self::ParseState::*; + let string = Some(string); + match state { + &On if self.on_date.is_none() => { + return Ok(CommandParser { + on_date: string, + ..self + }) + } + &At if self.at_time.is_none() => { + return Ok(CommandParser { + at_time: string, + ..self + }) + } + &In if self.in_duration.is_none() => { + return Ok(CommandParser { + in_duration: string, + ..self + }) + } + &Msg if self.message.is_none() => { + return Ok(CommandParser { + message: string, + ..self + }) + } + &Every if self.every_time.is_none() => { + return Ok(CommandParser { + every_time: string, + ..self + }) + } + _ => Err(ErrorKind::MissingMessage.into()), + } + } + + fn parse_date(&self, str_date: &str) -> Result { + let nums = str_date + .split('.') + .map(|s| s.parse::()) + .collect::, _>>() + .context(ErrorKind::InvalidDate)?; + + if 2 > nums.len() || nums.len() > 3 { + return Err(ErrorKind::InvalidDate.into()); + } + + let day = nums[0]; + let month = nums[1]; + + let parse_date = match nums.get(2) { + Some(year) => { + NaiveDate::from_ymd_opt(*year as i32, month, day).ok_or(ErrorKind::InvalidDate)? + } + None => { + let now = time::now(); + let date = NaiveDate::from_ymd_opt(now.tm_year + 1900, month, day) + .ok_or(ErrorKind::InvalidDate)?; + if date.succ().and_hms(0, 0, 0).timestamp() < now.to_timespec().sec { + NaiveDate::from_ymd(now.tm_year + 1901, month, day) + } else { + date + } + } + }; + + Ok(parse_date) + } + + fn parse_time(&self, str_time: &str) -> Result { + let nums = str_time + .split(':') + .map(|s| s.parse::()) + .collect::, _>>() + .context(ErrorKind::InvalidTime)?; + + if 2 != nums.len() { + return Err(ErrorKind::InvalidTime.into()); + } + + let hour = nums[0]; + let minute = nums[1]; + + Ok(NaiveTime::from_hms(hour, minute, 0)) + } + + pub fn get_time(&self, min_dur: Duration) -> Result { + if let Some(ref str_duration) = self.in_duration { + let duration = parse_duration(&str_duration).context(ErrorKind::InvalidTime)?; + + if duration < min_dur { + return Err(ErrorKind::TimeShort.into()); + } + + let tm = time::now().to_timespec(); + return Ok(NaiveDateTime::from_timestamp( + tm.sec + duration.as_secs() as i64, + 0u32, + )); + } + + let mut date = None; + if let Some(ref str_date) = self.on_date { + date = Some(self.parse_date(str_date)?); + } + + if let Some(ref str_time) = self.at_time { + let time = self.parse_time(str_time)?; + + if let Some(date) = date { + Ok(date.and_time(time)) + } else { + let now = time::now(); + let today = NaiveDate::from_ymd_opt( + now.tm_year + 1900, + now.tm_mon as u32 + 1, + now.tm_mday as u32, + ).ok_or(ErrorKind::InvalidDate)?; + + let time_today = today.and_time(time); + + if time_today.timestamp() < now.to_timespec().sec { + debug!("tomorrow"); + + Ok(today.succ().and_time(time)) + } else { + debug!("today"); + + Ok(time_today) + } + } + } else { + Ok(date.expect("At this point date has to be set") + .and_hms(0, 0, 0)) + } + } + + pub fn get_repeat(&self, min_dur: Duration) -> Result, RemindError> { + if let Some(mut words) = self.every_time.clone() { + if !words.chars().next().unwrap().is_digit(10) { + words.insert(0, '1'); + } + let dur = parse_duration(&words).context(ErrorKind::InvalidTime)?; + + if dur < min_dur { + return Err(ErrorKind::RepeatTimeShort.into()); + } + + Ok(Some(dur)) + } else { + Ok(None) + } + } + + pub fn get_target(&self) -> &str { + &self.target + } + + pub fn get_message(&self) -> &str { + self.message.as_ref().expect("Has to be set") + } +} -- cgit v1.2.3-70-g09d2 From 2d634608d69f3cf9c1287d08c1a509019cf55f65 Mon Sep 17 00:00:00 2001 From: Jokler Date: Sat, 16 Jun 2018 16:35:55 +0200 Subject: Currency: Remove plugin because of API changes The plugin was rarely used so fixing it would not be a priority. --- src/main.rs | 2 - src/plugins/currency.rs | 162 ------------------------------------------------ src/plugins/mod.rs | 1 - 3 files changed, 165 deletions(-) delete mode 100644 src/plugins/currency.rs (limited to 'src/plugins/mod.rs') diff --git a/src/main.rs b/src/main.rs index a21ff39..ad4c020 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,6 @@ use std::sync::Arc; use glob::glob; use irc::client::reactor::IrcReactor; -use frippy::plugins::currency::Currency; use frippy::plugins::emoji::Emoji; use frippy::plugins::factoids::Factoids; use frippy::plugins::help::Help; @@ -109,7 +108,6 @@ fn run() -> Result<(), Error> { bot.add_plugin(UrlTitles::new(1024)); bot.add_plugin(Sed::new(60)); bot.add_plugin(Emoji::new()); - bot.add_plugin(Currency::new()); bot.add_plugin(KeepNick::new()); #[cfg(feature = "mysql")] diff --git a/src/plugins/currency.rs b/src/plugins/currency.rs deleted file mode 100644 index 5790c70..0000000 --- a/src/plugins/currency.rs +++ /dev/null @@ -1,162 +0,0 @@ -extern crate reqwest; -extern crate serde; -extern crate serde_json; - -use std::io::Read; -use std::num::ParseFloatError; - -use irc::client::prelude::*; - -use self::reqwest::header::Connection; -use self::reqwest::Client; -use self::serde_json::Value; - -use plugin::*; - -use error::ErrorKind as FrippyErrorKind; -use error::FrippyError; -use failure::ResultExt; - -#[derive(PluginName, Default, Debug)] -pub struct Currency; - -struct ConvertionRequest<'a> { - value: f64, - source: &'a str, - target: &'a str, -} - -impl<'a> ConvertionRequest<'a> { - fn send(&self) -> Option { - let response = Client::new() - .get("https://api.fixer.io/latest") - .form(&[("base", self.source)]) - .header(Connection::close()) - .send(); - - match response { - Ok(mut response) => { - let mut body = String::new(); - response.read_to_string(&mut body).ok()?; - - let convertion_rates: Result = serde_json::from_str(&body); - match convertion_rates { - Ok(convertion_rates) => { - let rates: &Value = convertion_rates.get("rates")?; - let target_rate: &Value = rates.get(self.target.to_uppercase())?; - Some(self.value * target_rate.as_f64()?) - } - Err(_) => None, - } - } - Err(_) => None, - } - } -} - -impl Currency { - pub fn new() -> Currency { - Currency {} - } - - fn eval_command<'a>( - &self, - tokens: &'a [String], - ) -> Result, ParseFloatError> { - Ok(ConvertionRequest { - value: tokens[0].parse()?, - source: &tokens[1], - target: &tokens[2], - }) - } - - fn convert(&self, command: &mut PluginCommand) -> Result { - if command.tokens.len() < 3 { - return Err(self.invalid_command()); - } - - let request = match self.eval_command(&command.tokens) { - Ok(request) => request, - Err(_) => { - return Err(self.invalid_command()); - } - }; - - match request.send() { - Some(response) => { - let response = format!( - "{} {} => {:.4} {}", - request.value, - request.source.to_lowercase(), - response, - request.target.to_lowercase() - ); - - Ok(response) - } - None => Err("An error occured during the conversion of the given currency"), - } - } - - fn help(&self) -> &str { - "usage: currency value from_currency to_currency\r\n\ - example: currency 1.5 eur usd\r\n\ - available currencies: AUD, BGN, BRL, CAD, \ - CHF, CNY, CZK, DKK, GBP, HKD, HRK, HUF, \ - IDR, ILS, INR, JPY, KRW, MXN, MYR, NOK, \ - NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, \ - TRY, USD, ZAR" - } - - fn invalid_command(&self) -> &str { - "Incorrect Command. \ - Send \"currency help\" for help." - } -} - -impl Plugin for Currency { - fn execute(&self, _: &IrcClient, _: &Message) -> ExecutionStatus { - ExecutionStatus::Done - } - - fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> { - panic!("Currency does not implement the execute function!") - } - - fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), FrippyError> { - if command.tokens.is_empty() { - client - .send_notice(&command.source, &self.invalid_command()) - .context(FrippyErrorKind::Connection)?; - - return Ok(()); - } - - match command.tokens[0].as_ref() { - "help" => client - .send_notice(&command.source, self.help()) - .context(FrippyErrorKind::Connection)?, - _ => match self.convert(&mut command) { - Ok(msg) => client - .send_privmsg(&command.target, &msg) - .context(FrippyErrorKind::Connection)?, - Err(msg) => client - .send_notice(&command.source, &msg) - .context(FrippyErrorKind::Connection)?, - }, - } - - Ok(()) - } - - fn evaluate(&self, _: &IrcClient, mut command: PluginCommand) -> Result { - if command.tokens.is_empty() { - return Err(self.invalid_command().to_owned()); - } - - match command.tokens[0].as_ref() { - "help" => Ok(self.help().to_owned()), - _ => self.convert(&mut command).map_err(|e| e.to_owned()), - } - } -} diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 99e35db..05616f1 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -1,5 +1,4 @@ //! Collection of plugins included -pub mod currency; pub mod emoji; pub mod factoids; pub mod help; -- cgit v1.2.3-70-g09d2 From 625ca41cf54bac0268f7bde9d7ad9017c03d5919 Mon Sep 17 00:00:00 2001 From: Jokler Date: Fri, 21 Sep 2018 00:21:43 +0200 Subject: Quote: Add initial quote plugin --- Cargo.lock | 1 + Cargo.toml | 1 + .../2018-09-19-231843_create_quotes/down.sql | 1 + migrations/2018-09-19-231843_create_quotes/up.sql | 9 + src/error.rs | 4 + src/lib.rs | 1 + src/main.rs | 5 + src/plugins/factoids/mod.rs | 2 +- src/plugins/help.rs | 2 +- src/plugins/mod.rs | 1 + src/plugins/quote/database.rs | 136 +++++++++++ src/plugins/quote/mod.rs | 262 +++++++++++++++++++++ 12 files changed, 423 insertions(+), 2 deletions(-) create mode 100644 migrations/2018-09-19-231843_create_quotes/down.sql create mode 100644 migrations/2018-09-19-231843_create_quotes/up.sql create mode 100644 src/plugins/quote/database.rs create mode 100644 src/plugins/quote/mod.rs (limited to 'src/plugins/mod.rs') diff --git a/Cargo.lock b/Cargo.lock index a3fcb18..d5c4cc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -416,6 +416,7 @@ dependencies = [ "log4rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2-diesel 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rlua 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index a8c264f..a400bf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ antidote = "1.0.0" log4rs = "0.8.0" frippy_derive = { path = "frippy_derive" } +rand = "0.5.5" # TODO Use crates.io again, as soon as OpenSSL 1.1.1 is supported [dependencies.irc] diff --git a/migrations/2018-09-19-231843_create_quotes/down.sql b/migrations/2018-09-19-231843_create_quotes/down.sql new file mode 100644 index 0000000..b9d0f8b --- /dev/null +++ b/migrations/2018-09-19-231843_create_quotes/down.sql @@ -0,0 +1 @@ +DROP TABLE quotes diff --git a/migrations/2018-09-19-231843_create_quotes/up.sql b/migrations/2018-09-19-231843_create_quotes/up.sql new file mode 100644 index 0000000..357a63a --- /dev/null +++ b/migrations/2018-09-19-231843_create_quotes/up.sql @@ -0,0 +1,9 @@ +CREATE TABLE quotes ( + quotee VARCHAR(32) NOT NULL, + channel VARCHAR(32) NOT NULL, + idx INTEGER NOT NULL, + content TEXT NOT NULL, + author VARCHAR(32) NOT NULL, + created TIMESTAMP NOT NULL, + PRIMARY KEY (quotee, channel, idx) +) diff --git a/src/error.rs b/src/error.rs index 251e812..70a7724 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,6 +33,10 @@ pub enum ErrorKind { #[fail(display = "A Factoids error has occured")] Factoids, + /// A Quote error + #[fail(display = "A Quote error has occured")] + Quote, + /// A Remind error #[fail(display = "A Remind error has occured")] Remind, diff --git a/src/lib.rs b/src/lib.rs index b8662e4..c257544 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,7 @@ extern crate regex; extern crate reqwest; extern crate serde_json; extern crate time; +extern crate rand; pub mod error; pub mod plugin; diff --git a/src/main.rs b/src/main.rs index 9aed069..c337a46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,6 +31,7 @@ use irc::client::reactor::IrcReactor; use frippy::plugins::emoji::Emoji; use frippy::plugins::factoids::Factoids; +use frippy::plugins::quote::Quote; use frippy::plugins::help::Help; use frippy::plugins::keepnick::KeepNick; use frippy::plugins::remind::Remind; @@ -122,12 +123,14 @@ fn run() -> Result<(), Error> { Ok(_) => { let pool = Arc::new(pool); bot.add_plugin(Factoids::new(pool.clone())); + bot.add_plugin(Quote::new(pool.clone())); bot.add_plugin(Tell::new(pool.clone())); bot.add_plugin(Remind::new(pool.clone())); info!("Connected to MySQL server") } Err(e) => { bot.add_plugin(Factoids::new(HashMap::new())); + bot.add_plugin(Quote::new(HashMap::new())); bot.add_plugin(Tell::new(HashMap::new())); bot.add_plugin(Remind::new(HashMap::new())); error!("Failed to run migrations: {}", e); @@ -137,6 +140,7 @@ fn run() -> Result<(), Error> { } } else { bot.add_plugin(Factoids::new(HashMap::new())); + bot.add_plugin(Quote::new(HashMap::new())); bot.add_plugin(Tell::new(HashMap::new())); bot.add_plugin(Remind::new(HashMap::new())); } @@ -147,6 +151,7 @@ fn run() -> Result<(), Error> { error!("frippy was not built with the mysql feature") } bot.add_plugin(Factoids::new(HashMap::new())); + bot.add_plugin(Quote::new(HashMap::new())); bot.add_plugin(Tell::new(HashMap::new())); bot.add_plugin(Remind::new(HashMap::new())); } diff --git a/src/plugins/factoids/mod.rs b/src/plugins/factoids/mod.rs index 10f5131..a3d521a 100644 --- a/src/plugins/factoids/mod.rs +++ b/src/plugins/factoids/mod.rs @@ -228,7 +228,7 @@ impl Factoids { fn help(&self) -> &str { "usage: factoids \r\n\ - subcommands: add, fromurl, remove, info, get, exec, help" + subcommands: add, fromurl, remove, get, info, exec, help" } } diff --git a/src/plugins/help.rs b/src/plugins/help.rs index 5de2aca..1b50ca0 100644 --- a/src/plugins/help.rs +++ b/src/plugins/help.rs @@ -36,7 +36,7 @@ impl Plugin for Help { client .send_notice( &command.source, - "Available commands: help, tell, factoids, remind\r\n\ + "Available commands: help, tell, factoids, remind, quote\r\n\ For more detailed help call help on the specific command.\r\n\ Example: 'remind help'", ) diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 05616f1..e3bda60 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -1,6 +1,7 @@ //! Collection of plugins included pub mod emoji; pub mod factoids; +pub mod quote; pub mod help; pub mod keepnick; pub mod remind; diff --git a/src/plugins/quote/database.rs b/src/plugins/quote/database.rs new file mode 100644 index 0000000..6ad08a0 --- /dev/null +++ b/src/plugins/quote/database.rs @@ -0,0 +1,136 @@ +use std::collections::HashMap; +#[cfg(feature = "mysql")] +use std::sync::Arc; + +#[cfg(feature = "mysql")] +use diesel::mysql::MysqlConnection; +#[cfg(feature = "mysql")] +use diesel::prelude::*; +#[cfg(feature = "mysql")] +use failure::ResultExt; +#[cfg(feature = "mysql")] +use r2d2::Pool; +#[cfg(feature = "mysql")] +use r2d2_diesel::ConnectionManager; + +use chrono::NaiveDateTime; + +use super::error::*; + +#[cfg_attr(feature = "mysql", derive(Queryable))] +#[derive(Clone, Debug)] +pub struct Quote { + pub quotee: String, + pub channel: String, + pub idx: i32, + pub content: String, + pub author: String, + pub created: NaiveDateTime, +} + +#[cfg_attr(feature = "mysql", derive(Insertable))] +#[cfg_attr(feature = "mysql", table_name = "quotes")] +pub struct NewQuote<'a> { + pub quotee: &'a str, + pub channel: &'a str, + pub idx: i32, + pub content: &'a str, + pub author: &'a str, + pub created: NaiveDateTime, +} + +pub trait Database: Send + Sync { + fn insert_quote(&mut self, quote: &NewQuote) -> Result<(), QuoteError>; + fn get_quote(&self, quotee: &str, channel: &str, idx: i32) -> Result; + fn count_quotes(&self, quotee: &str, channel: &str) -> Result; +} + +// HashMap +impl Database for HashMap<(String, String, i32), Quote, S> { + fn insert_quote(&mut self, quote: &NewQuote) -> Result<(), QuoteError> { + let quote = Quote { + quotee: quote.quotee.to_owned(), + channel: quote.channel.to_owned(), + idx: quote.idx, + content: quote.content.to_owned(), + author: quote.author.to_owned(), + created: quote.created, + }; + + let quotee = quote.quotee.clone(); + let channel = quote.channel.clone(); + match self.insert((quotee, channel, quote.idx), quote) { + None => Ok(()), + Some(_) => Err(ErrorKind::Duplicate)?, + } + } + + fn get_quote(&self, quotee: &str, channel: &str, idx: i32) -> Result { + Ok(self.get(&(quotee.to_owned(), channel.to_owned(), idx)) + .cloned() + .ok_or(ErrorKind::NotFound)?) + } + + fn count_quotes(&self, quotee: &str, channel: &str) -> Result { + Ok(self.iter().filter(|&(&(ref n, ref c, _), _)| n == quotee && c == channel).count() as i32) + } +} + +// Diesel automatically defines the quotes module as public. +// We create a schema module to keep it private. +#[cfg(feature = "mysql")] +mod schema { + table! { + quotes (quotee, channel, idx) { + quotee -> Varchar, + channel -> Varchar, + idx -> Integer, + content -> Text, + author -> Varchar, + created -> Timestamp, + } + } +} + +#[cfg(feature = "mysql")] +use self::schema::quotes; + +#[cfg(feature = "mysql")] +impl Database for Arc>> { + fn insert_quote(&mut self, quote: &NewQuote) -> Result<(), QuoteError> { + use diesel; + + let conn = &*self.get().context(ErrorKind::NoConnection)?; + diesel::insert_into(quotes::table) + .values(quote) + .execute(conn) + .context(ErrorKind::MysqlError)?; + + Ok(()) + } + + fn get_quote(&self, quotee: &str, channel: &str, idx: i32) -> Result { + let conn = &*self.get().context(ErrorKind::NoConnection)?; + Ok(quotes::table + .find((quotee, channel, idx)) + .first(conn) + .context(ErrorKind::MysqlError)?) + } + + fn count_quotes(&self, quotee: &str, channel: &str) -> Result { + use diesel; + + let conn = &*self.get().context(ErrorKind::NoConnection)?; + let count: Result = quotes::table + .filter(quotes::columns::quotee.eq(quotee)) + .filter(quotes::columns::channel.eq(channel)) + .count() + .get_result(conn); + + match count { + Ok(c) => Ok(c as i32), + Err(diesel::NotFound) => Ok(0), + Err(e) => Err(e).context(ErrorKind::MysqlError)?, + } + } +} diff --git a/src/plugins/quote/mod.rs b/src/plugins/quote/mod.rs new file mode 100644 index 0000000..43333e7 --- /dev/null +++ b/src/plugins/quote/mod.rs @@ -0,0 +1,262 @@ +use std::fmt; +use std::marker::PhantomData; +use std::str::FromStr; + +use antidote::RwLock; +use irc::client::prelude::*; +use rand::{thread_rng, Rng}; +use chrono::NaiveDateTime; +use time; + +use plugin::*; +use FrippyClient; +pub mod database; +use self::database::Database; + +use self::error::*; +use error::ErrorKind as FrippyErrorKind; +use error::FrippyError; +use failure::ResultExt; + +enum QuoteResponse { + Public(String), + Private(String), +} + +#[derive(PluginName)] +pub struct Quote { + quotes: RwLock, + phantom: PhantomData, +} + +impl Quote { + pub fn new(db: T) -> Self { + Quote { + quotes: RwLock::new(db), + phantom: PhantomData, + } + } + + fn create_quote( + &self, + quotee: &str, + channel: &str, + content: &str, + author: &str, + ) -> Result<&str, QuoteError> { + let count = self.quotes.read().count_quotes(quotee, channel)?; + let tm = time::now().to_timespec(); + + let quote = database::NewQuote { + quotee, + channel, + idx: count + 1, + content, + author, + created: NaiveDateTime::from_timestamp(tm.sec, 0u32), + }; + + Ok(self.quotes + .write() + .insert_quote("e) + .map(|()| "Successfully added!")?) + } + + fn add(&self, command: &mut PluginCommand) -> Result<&str, QuoteError> { + if command.tokens.len() < 2 { + Err(ErrorKind::InvalidCommand)?; + } + + if command.target == command.source { + Err(ErrorKind::PrivateMessageNotAllowed)?; + } + + let quotee = command.tokens.remove(0); + let channel = &command.target; + let content = command.tokens.join(" "); + + Ok(self.create_quote("ee, channel, &content, &command.source)?) + } + + fn get(&self, command: &PluginCommand) -> Result { + if command.tokens.is_empty() { + Err(ErrorKind::InvalidCommand)?; + } + + let quotee = &command.tokens[0]; + let channel = &command.target; + let count = self.quotes.read().count_quotes(quotee, channel)?; + + if count < 1 { + Err(ErrorKind::NotFound)?; + } + + let idx = match command.tokens.len() { + 1 => thread_rng().gen_range(1, count + 1), + _ => { + let idx = match i32::from_str(&command.tokens[1]) { + Ok(i) => i, + Err(_) => Err(ErrorKind::InvalidIndex)?, + }; + + if idx < 0 { + count + idx + 1 + } else { + idx + } + } + }; + + let quote = self.quotes + .read() + .get_quote(quotee, channel, idx) + .context(ErrorKind::NotFound)?; + + + Ok(format!("\"{}\" - {}[{}/{}]", quote.content, quote.quotee, idx, count)) + } + + fn info(&self, command: &PluginCommand) -> Result { + match command.tokens.len() { + 0 => Err(ErrorKind::InvalidCommand)?, + 1 => { + let quotee = &command.tokens[0]; + let channel = &command.target; + let count = self.quotes.read().count_quotes(quotee, channel)?; + + Ok(match count { + 0 => Err(ErrorKind::NotFound)?, + 1 => format!("{} has 1 quote", quotee), + _ => format!("{} has {} quotes", quotee, count), + }) + } + _ => { + let quotee = &command.tokens[0]; + let channel = &command.target; + let idx = i32::from_str(&command.tokens[1]).context(ErrorKind::InvalidIndex)?; + let quote = self.quotes.read().get_quote(quotee, channel, idx)?; + + Ok(format!( + "{}'s quote was added by {} at {} UTC", + quotee, quote.author, quote.created + )) + } + } + } + + fn help(&self) -> &str { + "usage: quotes \r\n\ + subcommands: add, get, info, help" + } +} + +impl Plugin for Quote { + type Client = C; + fn execute(&self, _: &Self::Client, _: &Message) -> ExecutionStatus { + ExecutionStatus::Done + } + + fn execute_threaded(&self, _: &Self::Client, _: &Message) -> Result<(), FrippyError> { + panic!("Quotes should not use threading") + } + + fn command( + &self, + client: &Self::Client, + mut command: PluginCommand, + ) -> Result<(), FrippyError> { + use self::QuoteResponse::{Private, Public}; + + if command.tokens.is_empty() { + client + .send_notice(&command.source, &ErrorKind::InvalidCommand.to_string()) + .context(FrippyErrorKind::Connection)?; + + return Ok(()); + } + + let target = command.target.clone(); + let source = command.source.clone(); + + let sub_command = command.tokens.remove(0); + let result = match sub_command.as_ref() { + "add" => self.add(&mut command).map(|s| Private(s.to_owned())), + "get" => self.get(&command).map(Public), + "info" => self.info(&command).map(Public), + "help" => Ok(Private(self.help().to_owned())), + _ => Err(ErrorKind::InvalidCommand.into()), + }; + + match result { + Ok(v) => match v { + Public(m) => client + .send_privmsg(&target, &m) + .context(FrippyErrorKind::Connection)?, + Private(m) => client + .send_notice(&source, &m) + .context(FrippyErrorKind::Connection)?, + }, + Err(e) => { + let message = e.to_string(); + client + .send_notice(&source, &message) + .context(FrippyErrorKind::Connection)?; + Err(e).context(FrippyErrorKind::Quote)? + } + } + + Ok(()) + } + + fn evaluate(&self, _: &Self::Client, _: PluginCommand) -> Result { + Err(String::from( + "Evaluation of commands is not implemented for Quote at this time", + )) + } +} + +impl fmt::Debug for Quote { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Quote {{ ... }}") + } +} + +pub mod error { + #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)] + #[error = "QuoteError"] + pub enum ErrorKind { + /// Invalid command error + #[fail(display = "Incorrect command. Send \"quote help\" for help")] + InvalidCommand, + + /// Invalid index error + #[fail(display = "Invalid index")] + InvalidIndex, + + /// Private message error + #[fail(display = "You can only add quotes in channel messages")] + PrivateMessageNotAllowed, + + /// Download error + #[fail(display = "Download failed")] + Download, + + /// Duplicate error + #[fail(display = "Entry already exists")] + Duplicate, + + /// Not found error + #[fail(display = "Quote was not found")] + NotFound, + + /// MySQL error + #[cfg(feature = "mysql")] + #[fail(display = "Failed to execute MySQL Query")] + MysqlError, + + /// No connection error + #[cfg(feature = "mysql")] + #[fail(display = "No connection to the database")] + NoConnection, + } +} -- cgit v1.2.3-70-g09d2 From f9e3022756ea454a31f797bfc9cfdc1d81ee86cf Mon Sep 17 00:00:00 2001 From: Jokler Date: Mon, 22 Oct 2018 03:14:51 +0200 Subject: Factoids: Rename to Factoid --- src/error.rs | 9 +- src/main.rs | 15 +- src/plugins/emoji.rs | 3 +- src/plugins/factoid/database.rs | 161 +++++++++++++++++ src/plugins/factoid/mod.rs | 376 +++++++++++++++++++++++++++++++++++++++ src/plugins/factoid/sandbox.lua | 126 +++++++++++++ src/plugins/factoid/utils.rs | 82 +++++++++ src/plugins/factoids/database.rs | 160 ----------------- src/plugins/factoids/mod.rs | 373 -------------------------------------- src/plugins/factoids/sandbox.lua | 126 ------------- src/plugins/factoids/utils.rs | 81 --------- src/plugins/mod.rs | 4 +- 12 files changed, 761 insertions(+), 755 deletions(-) create mode 100644 src/plugins/factoid/database.rs create mode 100644 src/plugins/factoid/mod.rs create mode 100644 src/plugins/factoid/sandbox.lua create mode 100644 src/plugins/factoid/utils.rs delete mode 100644 src/plugins/factoids/database.rs delete mode 100644 src/plugins/factoids/mod.rs delete mode 100644 src/plugins/factoids/sandbox.lua delete mode 100644 src/plugins/factoids/utils.rs (limited to 'src/plugins/mod.rs') diff --git a/src/error.rs b/src/error.rs index 70a7724..039b71d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,7 +3,8 @@ use failure::Fail; pub fn log_error(e: &FrippyError) { - let text = e.causes() + let text = e + .causes() .skip(1) .fold(format!("{}", e), |acc, err| format!("{}: {}", acc, err)); error!("{}", text); @@ -29,9 +30,9 @@ pub enum ErrorKind { #[fail(display = "A Tell error has occured")] Tell, - /// A Factoids error - #[fail(display = "A Factoids error has occured")] - Factoids, + /// A Factoid error + #[fail(display = "A Factoid error has occured")] + Factoid, /// A Quote error #[fail(display = "A Quote error has occured")] diff --git a/src/main.rs b/src/main.rs index c337a46..03f3c7e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,10 +30,10 @@ use glob::glob; use irc::client::reactor::IrcReactor; use frippy::plugins::emoji::Emoji; -use frippy::plugins::factoids::Factoids; -use frippy::plugins::quote::Quote; +use frippy::plugins::factoid::Factoid; use frippy::plugins::help::Help; use frippy::plugins::keepnick::KeepNick; +use frippy::plugins::quote::Quote; use frippy::plugins::remind::Remind; use frippy::plugins::sed::Sed; use frippy::plugins::tell::Tell; @@ -58,7 +58,8 @@ fn main() { // Print any errors that caused frippy to shut down if let Err(e) = run() { - let text = e.iter_causes() + let text = e + .iter_causes() .fold(format!("{}", e), |acc, err| format!("{}: {}", acc, err)); error!("{}", text); } @@ -122,14 +123,14 @@ fn run() -> Result<(), Error> { Ok(pool) => match embedded_migrations::run(&*pool.get()?) { Ok(_) => { let pool = Arc::new(pool); - bot.add_plugin(Factoids::new(pool.clone())); + bot.add_plugin(Factoid::new(pool.clone())); bot.add_plugin(Quote::new(pool.clone())); bot.add_plugin(Tell::new(pool.clone())); bot.add_plugin(Remind::new(pool.clone())); info!("Connected to MySQL server") } Err(e) => { - bot.add_plugin(Factoids::new(HashMap::new())); + bot.add_plugin(Factoid::new(HashMap::new())); bot.add_plugin(Quote::new(HashMap::new())); bot.add_plugin(Tell::new(HashMap::new())); bot.add_plugin(Remind::new(HashMap::new())); @@ -139,7 +140,7 @@ fn run() -> Result<(), Error> { Err(e) => error!("Failed to connect to database: {}", e), } } else { - bot.add_plugin(Factoids::new(HashMap::new())); + bot.add_plugin(Factoid::new(HashMap::new())); bot.add_plugin(Quote::new(HashMap::new())); bot.add_plugin(Tell::new(HashMap::new())); bot.add_plugin(Remind::new(HashMap::new())); @@ -150,7 +151,7 @@ fn run() -> Result<(), Error> { if mysql_url.is_some() { error!("frippy was not built with the mysql feature") } - bot.add_plugin(Factoids::new(HashMap::new())); + bot.add_plugin(Factoid::new(HashMap::new())); bot.add_plugin(Quote::new(HashMap::new())); bot.add_plugin(Tell::new(HashMap::new())); bot.add_plugin(Remind::new(HashMap::new())); diff --git a/src/plugins/emoji.rs b/src/plugins/emoji.rs index d738110..aee61b1 100644 --- a/src/plugins/emoji.rs +++ b/src/plugins/emoji.rs @@ -137,8 +137,7 @@ impl Plugin for Emoji { .send_notice( &command.source, "This Plugin does not implement any commands.", - ) - .context(FrippyErrorKind::Connection)?; + ).context(FrippyErrorKind::Connection)?; Ok(()) } diff --git a/src/plugins/factoid/database.rs b/src/plugins/factoid/database.rs new file mode 100644 index 0000000..5e7e24c --- /dev/null +++ b/src/plugins/factoid/database.rs @@ -0,0 +1,161 @@ +use std::collections::HashMap; +#[cfg(feature = "mysql")] +use std::sync::Arc; + +#[cfg(feature = "mysql")] +use diesel::mysql::MysqlConnection; +#[cfg(feature = "mysql")] +use diesel::prelude::*; +#[cfg(feature = "mysql")] +use failure::ResultExt; +#[cfg(feature = "mysql")] +use r2d2::Pool; +#[cfg(feature = "mysql")] +use r2d2_diesel::ConnectionManager; + +use chrono::NaiveDateTime; + +use super::error::*; + +#[cfg_attr(feature = "mysql", derive(Queryable))] +#[derive(Clone, Debug)] +pub struct Factoid { + pub name: String, + pub idx: i32, + pub content: String, + pub author: String, + pub created: NaiveDateTime, +} + +#[cfg_attr(feature = "mysql", derive(Insertable))] +#[cfg_attr(feature = "mysql", table_name = "factoids")] +pub struct NewFactoid<'a> { + pub name: &'a str, + pub idx: i32, + pub content: &'a str, + pub author: &'a str, + pub created: NaiveDateTime, +} + +pub trait Database: Send + Sync { + fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidError>; + fn get_factoid(&self, name: &str, idx: i32) -> Result; + fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidError>; + fn count_factoids(&self, name: &str) -> Result; +} + +// HashMap +impl Database for HashMap<(String, i32), Factoid, S> { + fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidError> { + let factoid = Factoid { + name: factoid.name.to_owned(), + idx: factoid.idx, + content: factoid.content.to_owned(), + author: factoid.author.to_owned(), + created: factoid.created, + }; + + let name = factoid.name.clone(); + match self.insert((name, factoid.idx), factoid) { + None => Ok(()), + Some(_) => Err(ErrorKind::Duplicate)?, + } + } + + fn get_factoid(&self, name: &str, idx: i32) -> Result { + Ok(self + .get(&(name.to_owned(), idx)) + .cloned() + .ok_or(ErrorKind::NotFound)?) + } + + fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidError> { + match self.remove(&(name.to_owned(), idx)) { + Some(_) => Ok(()), + None => Err(ErrorKind::NotFound)?, + } + } + + fn count_factoids(&self, name: &str) -> Result { + Ok(self.iter().filter(|&(&(ref n, _), _)| n == name).count() as i32) + } +} + +// Diesel automatically defines the factoids module as public. +// We create a schema module to keep it private. +#[cfg(feature = "mysql")] +mod schema { + table! { + factoids (name, idx) { + name -> Varchar, + idx -> Integer, + content -> Text, + author -> Varchar, + created -> Timestamp, + } + } +} + +#[cfg(feature = "mysql")] +use self::schema::factoids; + +#[cfg(feature = "mysql")] +impl Database for Arc>> { + fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidError> { + use diesel; + + let conn = &*self.get().context(ErrorKind::NoConnection)?; + diesel::insert_into(factoids::table) + .values(factoid) + .execute(conn) + .context(ErrorKind::MysqlError)?; + + Ok(()) + } + + fn get_factoid(&self, name: &str, idx: i32) -> Result { + let conn = &*self.get().context(ErrorKind::NoConnection)?; + Ok(factoids::table + .find((name, idx)) + .first(conn) + .context(ErrorKind::MysqlError)?) + } + + fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidError> { + use self::factoids::columns; + use diesel; + + let conn = &*self.get().context(ErrorKind::NoConnection)?; + match diesel::delete( + factoids::table + .filter(columns::name.eq(name)) + .filter(columns::idx.eq(idx)), + ).execute(conn) + { + Ok(v) => { + if v > 0 { + Ok(()) + } else { + Err(ErrorKind::NotFound)? + } + } + Err(e) => Err(e).context(ErrorKind::MysqlError)?, + } + } + + fn count_factoids(&self, name: &str) -> Result { + use diesel; + + let conn = &*self.get().context(ErrorKind::NoConnection)?; + let count: Result = factoids::table + .filter(factoids::columns::name.eq(name)) + .count() + .get_result(conn); + + match count { + Ok(c) => Ok(c as i32), + Err(diesel::NotFound) => Ok(0), + Err(e) => Err(e).context(ErrorKind::MysqlError)?, + } + } +} diff --git a/src/plugins/factoid/mod.rs b/src/plugins/factoid/mod.rs new file mode 100644 index 0000000..4fcc7a0 --- /dev/null +++ b/src/plugins/factoid/mod.rs @@ -0,0 +1,376 @@ +extern crate rlua; + +use self::rlua::prelude::*; +use antidote::RwLock; +use irc::client::prelude::*; +use std::fmt; +use std::marker::PhantomData; +use std::str::FromStr; + +use chrono::NaiveDateTime; +use time; + +use plugin::*; +use FrippyClient; +pub mod database; +use self::database::Database; + +mod utils; +use self::utils::*; +use utils::Url; + +use self::error::*; +use error::ErrorKind as FrippyErrorKind; +use error::FrippyError; +use failure::ResultExt; + +static LUA_SANDBOX: &'static str = include_str!("sandbox.lua"); + +enum FactoidResponse { + Public(String), + Private(String), +} + +#[derive(PluginName)] +pub struct Factoid { + factoids: RwLock, + phantom: PhantomData, +} + +impl Factoid { + pub fn new(db: T) -> Self { + Factoid { + factoids: RwLock::new(db), + phantom: PhantomData, + } + } + + fn create_factoid( + &self, + name: &str, + content: &str, + author: &str, + ) -> Result<&str, FactoidError> { + let count = self.factoids.read().count_factoids(name)?; + let tm = time::now().to_timespec(); + + let factoid = database::NewFactoid { + name, + idx: count, + content, + author, + created: NaiveDateTime::from_timestamp(tm.sec, 0u32), + }; + + Ok(self + .factoids + .write() + .insert_factoid(&factoid) + .map(|()| "Successfully added!")?) + } + + fn add(&self, command: &mut PluginCommand) -> Result<&str, FactoidError> { + if command.tokens.len() < 2 { + Err(ErrorKind::InvalidCommand)?; + } + + let name = command.tokens.remove(0); + let content = command.tokens.join(" "); + + Ok(self.create_factoid(&name, &content, &command.source)?) + } + + fn add_from_url(&self, command: &mut PluginCommand) -> Result<&str, FactoidError> { + if command.tokens.len() < 2 { + Err(ErrorKind::InvalidCommand)?; + } + + let name = command.tokens.remove(0); + let url = &command.tokens[0]; + let content = Url::from(url.as_ref()) + .max_kib(1024) + .request() + .context(ErrorKind::Download)?; + + Ok(self.create_factoid(&name, &content, &command.source)?) + } + + fn remove(&self, command: &mut PluginCommand) -> Result<&str, FactoidError> { + if command.tokens.is_empty() { + Err(ErrorKind::InvalidCommand)?; + } + + let name = command.tokens.remove(0); + let count = self.factoids.read().count_factoids(&name)?; + + match self.factoids.write().delete_factoid(&name, count - 1) { + Ok(()) => Ok("Successfully removed"), + Err(e) => Err(e)?, + } + } + + fn get(&self, command: &PluginCommand) -> Result { + let (name, idx) = match command.tokens.len() { + 0 => Err(ErrorKind::InvalidCommand)?, + 1 => { + let name = &command.tokens[0]; + let count = self.factoids.read().count_factoids(name)?; + + if count < 1 { + Err(ErrorKind::NotFound)?; + } + + (name, count - 1) + } + _ => { + let name = &command.tokens[0]; + let idx = match i32::from_str(&command.tokens[1]) { + Ok(i) => i, + Err(_) => Err(ErrorKind::InvalidCommand)?, + }; + + (name, idx) + } + }; + + let factoid = self + .factoids + .read() + .get_factoid(name, idx) + .context(ErrorKind::NotFound)?; + + let message = factoid.content.replace("\n", "|").replace("\r", ""); + + Ok(format!("{}: {}", factoid.name, message)) + } + + fn info(&self, command: &PluginCommand) -> Result { + match command.tokens.len() { + 0 => Err(ErrorKind::InvalidCommand)?, + 1 => { + let name = &command.tokens[0]; + let count = self.factoids.read().count_factoids(name)?; + + Ok(match count { + 0 => Err(ErrorKind::NotFound)?, + 1 => format!("There is 1 version of {}", name), + _ => format!("There are {} versions of {}", count, name), + }) + } + _ => { + let name = &command.tokens[0]; + let idx = i32::from_str(&command.tokens[1]).context(ErrorKind::InvalidIndex)?; + let factoid = self.factoids.read().get_factoid(name, idx)?; + + Ok(format!( + "{}: Added by {} at {} UTC", + name, factoid.author, factoid.created + )) + } + } + } + + fn exec(&self, mut command: PluginCommand) -> Result { + if command.tokens.is_empty() { + Err(ErrorKind::InvalidIndex)? + } else { + let name = command.tokens.remove(0); + let count = self.factoids.read().count_factoids(&name)?; + let factoid = self.factoids.read().get_factoid(&name, count - 1)?; + + let content = factoid.content; + let value = if content.starts_with('>') { + let content = String::from(&content[1..]); + + if content.starts_with('>') { + content + } else { + match self.run_lua(&name, &content, &command) { + Ok(v) => v, + Err(e) => match e { + LuaError::CallbackError { cause, .. } => cause.to_string(), + _ => e.to_string(), + }, + } + } + } else { + content + }; + + Ok(value.replace("\n", "|").replace("\r", "")) + } + } + + fn run_lua(&self, name: &str, code: &str, command: &PluginCommand) -> Result { + let args = command + .tokens + .iter() + .filter(|x| !x.is_empty()) + .map(ToOwned::to_owned) + .collect::>(); + + let lua = unsafe { Lua::new_with_debug() }; + let globals = lua.globals(); + + globals.set("factoid", code)?; + globals.set("download", lua.create_function(download)?)?; + globals.set("json_decode", lua.create_function(json_decode)?)?; + globals.set("sleep", lua.create_function(sleep)?)?; + globals.set("args", args)?; + globals.set("input", command.tokens.join(" "))?; + globals.set("user", command.source.clone())?; + globals.set("channel", command.target.clone())?; + globals.set("output", lua.create_table()?)?; + + lua.exec::<()>(LUA_SANDBOX, Some(name))?; + let output: Vec = globals.get::<_, Vec>("output")?; + + Ok(output.join("|")) + } + + fn help(&self) -> &str { + "usage: factoids \r\n\ + subcommands: add, fromurl, remove, get, info, exec, help" + } +} + +impl Plugin for Factoid { + type Client = C; + fn execute(&self, _: &Self::Client, message: &Message) -> ExecutionStatus { + match message.command { + Command::PRIVMSG(_, ref content) => if content.starts_with('!') { + ExecutionStatus::RequiresThread + } else { + ExecutionStatus::Done + }, + _ => ExecutionStatus::Done, + } + } + + fn execute_threaded( + &self, + client: &Self::Client, + message: &Message, + ) -> Result<(), FrippyError> { + if let Command::PRIVMSG(_, mut content) = message.command.clone() { + content.remove(0); + + let t: Vec = content.split(' ').map(ToOwned::to_owned).collect(); + + let c = PluginCommand { + source: message.source_nickname().unwrap().to_owned(), + target: message.response_target().unwrap().to_owned(), + tokens: t, + }; + + if let Ok(f) = self.exec(c) { + client + .send_privmsg(&message.response_target().unwrap(), &f) + .context(FrippyErrorKind::Connection)?; + } + } + + Ok(()) + } + + fn command( + &self, + client: &Self::Client, + mut command: PluginCommand, + ) -> Result<(), FrippyError> { + use self::FactoidResponse::{Private, Public}; + + if command.tokens.is_empty() { + client + .send_notice(&command.source, "Invalid command") + .context(FrippyErrorKind::Connection)?; + + return Ok(()); + } + + let target = command.target.clone(); + let source = command.source.clone(); + + let sub_command = command.tokens.remove(0); + let result = match sub_command.as_ref() { + "add" => self.add(&mut command).map(|s| Private(s.to_owned())), + "fromurl" => self + .add_from_url(&mut command) + .map(|s| Private(s.to_owned())), + "remove" => self.remove(&mut command).map(|s| Private(s.to_owned())), + "get" => self.get(&command).map(Public), + "info" => self.info(&command).map(Public), + "exec" => self.exec(command).map(Public), + "help" => Ok(Private(self.help().to_owned())), + _ => Err(ErrorKind::InvalidCommand.into()), + }; + + match result { + Ok(v) => match v { + Public(m) => client + .send_privmsg(&target, &m) + .context(FrippyErrorKind::Connection)?, + Private(m) => client + .send_notice(&source, &m) + .context(FrippyErrorKind::Connection)?, + }, + Err(e) => { + let message = e.to_string(); + client + .send_notice(&source, &message) + .context(FrippyErrorKind::Connection)?; + Err(e).context(FrippyErrorKind::Factoid)? + } + } + + Ok(()) + } + + fn evaluate(&self, _: &Self::Client, _: PluginCommand) -> Result { + Err(String::from( + "Evaluation of commands is not implemented for Factoid at this time", + )) + } +} + +impl fmt::Debug for Factoid { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Factoid {{ ... }}") + } +} + +pub mod error { + #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)] + #[error = "FactoidError"] + pub enum ErrorKind { + /// Invalid command error + #[fail(display = "Invalid Command")] + InvalidCommand, + + /// Invalid index error + #[fail(display = "Invalid index")] + InvalidIndex, + + /// Download error + #[fail(display = "Download failed")] + Download, + + /// Duplicate error + #[fail(display = "Entry already exists")] + Duplicate, + + /// Not found error + #[fail(display = "Factoid was not found")] + NotFound, + + /// MySQL error + #[cfg(feature = "mysql")] + #[fail(display = "Failed to execute MySQL Query")] + MysqlError, + + /// No connection error + #[cfg(feature = "mysql")] + #[fail(display = "No connection to the database")] + NoConnection, + } +} diff --git a/src/plugins/factoid/sandbox.lua b/src/plugins/factoid/sandbox.lua new file mode 100644 index 0000000..a927535 --- /dev/null +++ b/src/plugins/factoid/sandbox.lua @@ -0,0 +1,126 @@ +function send(text) + local text = tostring(text) + local len = #output + if len < 1 then + output = { text } + else + output[len] = output[len] .. text + end +end + +function sendln(text) + send(text) + table.insert(output, "") +end + +function trim(s) + local from = s:match"^%s*()" + return from > #s and "" or s:match(".*%S", from) +end + +trimmedInput = trim(input) + +if trimmedInput == "" then + ioru = user +else + ioru = trimmedInput +end + +local sandbox_env = { + print = send, + println = sendln, + trim = trim, + eval = nil, + sleep = nil, + json = {decode = json_decode}, + args = args, + input = input, + user = user, + ioru = ioru, + channel = channel, + request = download, + string = string, + math = math, + table = table, + pairs = pairs, + ipairs = ipairs, + next = next, + select = select, + unpack = unpack, + tostring = tostring, + tonumber = tonumber, + type = type, + assert = assert, + error = error, + pcall = pcall, + xpcall = xpcall, + _VERSION = _VERSION +} + +sandbox_env.os = { + clock = os.clock, + time = os.time, + difftime = os.difftime +} + +sandbox_env.string.rep = nil +sandbox_env.string.dump = nil +sandbox_env.math.randomseed = nil + +-- Temporary evaluation function +function eval(code) + local c, e = load(code, nil, nil, sandbox_env) + if c then + return c() + else + error(e) + end +end + +-- Only sleeps for 1 second at a time +-- This ensures that the timeout check can still run +function safesleep(dur) + while dur > 1000 do + dur = dur - 1000 + sleep(1000) + end + sleep(dur) +end + +sandbox_env.eval = eval +sandbox_env.sleep = safesleep + +-- Check if the factoid timed out +function checktime() + if os.time() - time >= timeout then + error("Timed out after " .. timeout .. " seconds", 0) + else + -- Limit the cpu usage of factoids + sleep(1) + end +end + +-- Check if the factoid uses too much memory +function checkmem() + if collectgarbage("count") > maxmem then + error("Factoid used over " .. maxmem .. " kbyte of ram") + end +end + +local f, e = load(factoid, nil, nil, sandbox_env) + +-- Add timeout hook +time = os.time() +-- The timeout is defined in seconds +timeout = 30 +debug.sethook(checktime, "l") +-- Add memory check hook +-- The max memory is defined in kilobytes +maxmem = 1000 +debug.sethook(checkmem, "l") + +if f then + f() +else + error(e) +end diff --git a/src/plugins/factoid/utils.rs b/src/plugins/factoid/utils.rs new file mode 100644 index 0000000..7bd9b20 --- /dev/null +++ b/src/plugins/factoid/utils.rs @@ -0,0 +1,82 @@ +use std::thread; +use std::time::Duration; + +use serde_json::{self, Value as SerdeValue}; + +use super::rlua::Error as LuaError; +use super::rlua::Error::RuntimeError; +use super::rlua::{Lua, Value as LuaValue}; + +use utils::error::ErrorKind::Connection; +use utils::Url; + +use failure::Fail; + +pub fn sleep(_: &Lua, dur: u64) -> Result<(), LuaError> { + thread::sleep(Duration::from_millis(dur)); + Ok(()) +} + +pub fn download(_: &Lua, url: String) -> Result { + let url = Url::from(url).max_kib(1024); + match url.request() { + Ok(v) => Ok(v), + Err(e) => { + let error = match e.kind() { + Connection => e.cause().unwrap().to_string(), + _ => e.to_string(), + }; + + Err(RuntimeError(format!( + "Failed to download {} - {}", + url.as_str(), + error + ))) + } + } +} + +fn convert_value(lua: &Lua, sval: SerdeValue, max_recurs: usize) -> Result { + if max_recurs == 0 { + return Err(RuntimeError(String::from( + "Reached max recursion level - json is nested too deep", + ))); + } + + let lval = match sval { + SerdeValue::Null => LuaValue::Nil, + SerdeValue::Bool(b) => LuaValue::Boolean(b), + SerdeValue::String(s) => LuaValue::String(lua.create_string(&s)?), + SerdeValue::Number(n) => { + let f = n.as_f64().ok_or_else(|| { + RuntimeError(String::from("Failed to convert number into double")) + })?; + LuaValue::Number(f) + } + SerdeValue::Array(arr) => { + let table = lua.create_table()?; + for (i, val) in arr.into_iter().enumerate() { + table.set(i + 1, convert_value(lua, val, max_recurs - 1)?)?; + } + + LuaValue::Table(table) + } + SerdeValue::Object(obj) => { + let table = lua.create_table()?; + for (key, val) in obj { + table.set(key, convert_value(lua, val, max_recurs - 1)?)?; + } + + LuaValue::Table(table) + } + }; + + Ok(lval) +} + +pub fn json_decode(lua: &Lua, json: String) -> Result { + let ser_val: SerdeValue = + serde_json::from_str(&json).map_err(|e| RuntimeError(e.to_string()))?; + + convert_value(lua, ser_val, 25) +} diff --git a/src/plugins/factoids/database.rs b/src/plugins/factoids/database.rs deleted file mode 100644 index ec8ed3e..0000000 --- a/src/plugins/factoids/database.rs +++ /dev/null @@ -1,160 +0,0 @@ -use std::collections::HashMap; -#[cfg(feature = "mysql")] -use std::sync::Arc; - -#[cfg(feature = "mysql")] -use diesel::mysql::MysqlConnection; -#[cfg(feature = "mysql")] -use diesel::prelude::*; -#[cfg(feature = "mysql")] -use failure::ResultExt; -#[cfg(feature = "mysql")] -use r2d2::Pool; -#[cfg(feature = "mysql")] -use r2d2_diesel::ConnectionManager; - -use chrono::NaiveDateTime; - -use super::error::*; - -#[cfg_attr(feature = "mysql", derive(Queryable))] -#[derive(Clone, Debug)] -pub struct Factoid { - pub name: String, - pub idx: i32, - pub content: String, - pub author: String, - pub created: NaiveDateTime, -} - -#[cfg_attr(feature = "mysql", derive(Insertable))] -#[cfg_attr(feature = "mysql", table_name = "factoids")] -pub struct NewFactoid<'a> { - pub name: &'a str, - pub idx: i32, - pub content: &'a str, - pub author: &'a str, - pub created: NaiveDateTime, -} - -pub trait Database: Send + Sync { - fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidsError>; - fn get_factoid(&self, name: &str, idx: i32) -> Result; - fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError>; - fn count_factoids(&self, name: &str) -> Result; -} - -// HashMap -impl Database for HashMap<(String, i32), Factoid, S> { - fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidsError> { - let factoid = Factoid { - name: factoid.name.to_owned(), - idx: factoid.idx, - content: factoid.content.to_owned(), - author: factoid.author.to_owned(), - created: factoid.created, - }; - - let name = factoid.name.clone(); - match self.insert((name, factoid.idx), factoid) { - None => Ok(()), - Some(_) => Err(ErrorKind::Duplicate)?, - } - } - - fn get_factoid(&self, name: &str, idx: i32) -> Result { - Ok(self.get(&(name.to_owned(), idx)) - .cloned() - .ok_or(ErrorKind::NotFound)?) - } - - fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError> { - match self.remove(&(name.to_owned(), idx)) { - Some(_) => Ok(()), - None => Err(ErrorKind::NotFound)?, - } - } - - fn count_factoids(&self, name: &str) -> Result { - Ok(self.iter().filter(|&(&(ref n, _), _)| n == name).count() as i32) - } -} - -// Diesel automatically defines the factoids module as public. -// We create a schema module to keep it private. -#[cfg(feature = "mysql")] -mod schema { - table! { - factoids (name, idx) { - name -> Varchar, - idx -> Integer, - content -> Text, - author -> Varchar, - created -> Timestamp, - } - } -} - -#[cfg(feature = "mysql")] -use self::schema::factoids; - -#[cfg(feature = "mysql")] -impl Database for Arc>> { - fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidsError> { - use diesel; - - let conn = &*self.get().context(ErrorKind::NoConnection)?; - diesel::insert_into(factoids::table) - .values(factoid) - .execute(conn) - .context(ErrorKind::MysqlError)?; - - Ok(()) - } - - fn get_factoid(&self, name: &str, idx: i32) -> Result { - let conn = &*self.get().context(ErrorKind::NoConnection)?; - Ok(factoids::table - .find((name, idx)) - .first(conn) - .context(ErrorKind::MysqlError)?) - } - - fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError> { - use self::factoids::columns; - use diesel; - - let conn = &*self.get().context(ErrorKind::NoConnection)?; - match diesel::delete( - factoids::table - .filter(columns::name.eq(name)) - .filter(columns::idx.eq(idx)), - ).execute(conn) - { - Ok(v) => { - if v > 0 { - Ok(()) - } else { - Err(ErrorKind::NotFound)? - } - } - Err(e) => Err(e).context(ErrorKind::MysqlError)?, - } - } - - fn count_factoids(&self, name: &str) -> Result { - use diesel; - - let conn = &*self.get().context(ErrorKind::NoConnection)?; - let count: Result = factoids::table - .filter(factoids::columns::name.eq(name)) - .count() - .get_result(conn); - - match count { - Ok(c) => Ok(c as i32), - Err(diesel::NotFound) => Ok(0), - Err(e) => Err(e).context(ErrorKind::MysqlError)?, - } - } -} diff --git a/src/plugins/factoids/mod.rs b/src/plugins/factoids/mod.rs deleted file mode 100644 index a3d521a..0000000 --- a/src/plugins/factoids/mod.rs +++ /dev/null @@ -1,373 +0,0 @@ -extern crate rlua; - -use self::rlua::prelude::*; -use antidote::RwLock; -use irc::client::prelude::*; -use std::fmt; -use std::marker::PhantomData; -use std::str::FromStr; - -use chrono::NaiveDateTime; -use time; - -use plugin::*; -use FrippyClient; -pub mod database; -use self::database::Database; - -mod utils; -use self::utils::*; -use utils::Url; - -use self::error::*; -use error::ErrorKind as FrippyErrorKind; -use error::FrippyError; -use failure::ResultExt; - -static LUA_SANDBOX: &'static str = include_str!("sandbox.lua"); - -enum FactoidResponse { - Public(String), - Private(String), -} - -#[derive(PluginName)] -pub struct Factoids { - factoids: RwLock, - phantom: PhantomData, -} - -impl Factoids { - pub fn new(db: T) -> Self { - Factoids { - factoids: RwLock::new(db), - phantom: PhantomData, - } - } - - fn create_factoid( - &self, - name: &str, - content: &str, - author: &str, - ) -> Result<&str, FactoidsError> { - let count = self.factoids.read().count_factoids(name)?; - let tm = time::now().to_timespec(); - - let factoid = database::NewFactoid { - name, - idx: count, - content, - author, - created: NaiveDateTime::from_timestamp(tm.sec, 0u32), - }; - - Ok(self.factoids - .write() - .insert_factoid(&factoid) - .map(|()| "Successfully added!")?) - } - - fn add(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> { - if command.tokens.len() < 2 { - Err(ErrorKind::InvalidCommand)?; - } - - let name = command.tokens.remove(0); - let content = command.tokens.join(" "); - - Ok(self.create_factoid(&name, &content, &command.source)?) - } - - fn add_from_url(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> { - if command.tokens.len() < 2 { - Err(ErrorKind::InvalidCommand)?; - } - - let name = command.tokens.remove(0); - let url = &command.tokens[0]; - let content = Url::from(url.as_ref()) - .max_kib(1024) - .request() - .context(ErrorKind::Download)?; - - Ok(self.create_factoid(&name, &content, &command.source)?) - } - - fn remove(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> { - if command.tokens.is_empty() { - Err(ErrorKind::InvalidCommand)?; - } - - let name = command.tokens.remove(0); - let count = self.factoids.read().count_factoids(&name)?; - - match self.factoids.write().delete_factoid(&name, count - 1) { - Ok(()) => Ok("Successfully removed"), - Err(e) => Err(e)?, - } - } - - fn get(&self, command: &PluginCommand) -> Result { - let (name, idx) = match command.tokens.len() { - 0 => Err(ErrorKind::InvalidCommand)?, - 1 => { - let name = &command.tokens[0]; - let count = self.factoids.read().count_factoids(name)?; - - if count < 1 { - Err(ErrorKind::NotFound)?; - } - - (name, count - 1) - } - _ => { - let name = &command.tokens[0]; - let idx = match i32::from_str(&command.tokens[1]) { - Ok(i) => i, - Err(_) => Err(ErrorKind::InvalidCommand)?, - }; - - (name, idx) - } - }; - - let factoid = self.factoids - .read() - .get_factoid(name, idx) - .context(ErrorKind::NotFound)?; - - let message = factoid.content.replace("\n", "|").replace("\r", ""); - - Ok(format!("{}: {}", factoid.name, message)) - } - - fn info(&self, command: &PluginCommand) -> Result { - match command.tokens.len() { - 0 => Err(ErrorKind::InvalidCommand)?, - 1 => { - let name = &command.tokens[0]; - let count = self.factoids.read().count_factoids(name)?; - - Ok(match count { - 0 => Err(ErrorKind::NotFound)?, - 1 => format!("There is 1 version of {}", name), - _ => format!("There are {} versions of {}", count, name), - }) - } - _ => { - let name = &command.tokens[0]; - let idx = i32::from_str(&command.tokens[1]).context(ErrorKind::InvalidIndex)?; - let factoid = self.factoids.read().get_factoid(name, idx)?; - - Ok(format!( - "{}: Added by {} at {} UTC", - name, factoid.author, factoid.created - )) - } - } - } - - fn exec(&self, mut command: PluginCommand) -> Result { - if command.tokens.is_empty() { - Err(ErrorKind::InvalidIndex)? - } else { - let name = command.tokens.remove(0); - let count = self.factoids.read().count_factoids(&name)?; - let factoid = self.factoids.read().get_factoid(&name, count - 1)?; - - let content = factoid.content; - let value = if content.starts_with('>') { - let content = String::from(&content[1..]); - - if content.starts_with('>') { - content - } else { - match self.run_lua(&name, &content, &command) { - Ok(v) => v, - Err(e) => match e { - LuaError::CallbackError { cause, .. } => cause.to_string(), - _ => e.to_string(), - }, - } - } - } else { - content - }; - - Ok(value.replace("\n", "|").replace("\r", "")) - } - } - - fn run_lua(&self, name: &str, code: &str, command: &PluginCommand) -> Result { - let args = command - .tokens - .iter() - .filter(|x| !x.is_empty()) - .map(ToOwned::to_owned) - .collect::>(); - - let lua = unsafe { Lua::new_with_debug() }; - let globals = lua.globals(); - - globals.set("factoid", code)?; - globals.set("download", lua.create_function(download)?)?; - globals.set("json_decode", lua.create_function(json_decode)?)?; - globals.set("sleep", lua.create_function(sleep)?)?; - globals.set("args", args)?; - globals.set("input", command.tokens.join(" "))?; - globals.set("user", command.source.clone())?; - globals.set("channel", command.target.clone())?; - globals.set("output", lua.create_table()?)?; - - lua.exec::<()>(LUA_SANDBOX, Some(name))?; - let output: Vec = globals.get::<_, Vec>("output")?; - - Ok(output.join("|")) - } - - fn help(&self) -> &str { - "usage: factoids \r\n\ - subcommands: add, fromurl, remove, get, info, exec, help" - } -} - -impl Plugin for Factoids { - type Client = C; - fn execute(&self, _: &Self::Client, message: &Message) -> ExecutionStatus { - match message.command { - Command::PRIVMSG(_, ref content) => if content.starts_with('!') { - ExecutionStatus::RequiresThread - } else { - ExecutionStatus::Done - }, - _ => ExecutionStatus::Done, - } - } - - fn execute_threaded( - &self, - client: &Self::Client, - message: &Message, - ) -> Result<(), FrippyError> { - if let Command::PRIVMSG(_, mut content) = message.command.clone() { - content.remove(0); - - let t: Vec = content.split(' ').map(ToOwned::to_owned).collect(); - - let c = PluginCommand { - source: message.source_nickname().unwrap().to_owned(), - target: message.response_target().unwrap().to_owned(), - tokens: t, - }; - - if let Ok(f) = self.exec(c) { - client - .send_privmsg(&message.response_target().unwrap(), &f) - .context(FrippyErrorKind::Connection)?; - } - } - - Ok(()) - } - - fn command( - &self, - client: &Self::Client, - mut command: PluginCommand, - ) -> Result<(), FrippyError> { - use self::FactoidResponse::{Private, Public}; - - if command.tokens.is_empty() { - client - .send_notice(&command.source, "Invalid command") - .context(FrippyErrorKind::Connection)?; - - return Ok(()); - } - - let target = command.target.clone(); - let source = command.source.clone(); - - let sub_command = command.tokens.remove(0); - let result = match sub_command.as_ref() { - "add" => self.add(&mut command).map(|s| Private(s.to_owned())), - "fromurl" => self.add_from_url(&mut command) - .map(|s| Private(s.to_owned())), - "remove" => self.remove(&mut command).map(|s| Private(s.to_owned())), - "get" => self.get(&command).map(Public), - "info" => self.info(&command).map(Public), - "exec" => self.exec(command).map(Public), - "help" => Ok(Private(self.help().to_owned())), - _ => Err(ErrorKind::InvalidCommand.into()), - }; - - match result { - Ok(v) => match v { - Public(m) => client - .send_privmsg(&target, &m) - .context(FrippyErrorKind::Connection)?, - Private(m) => client - .send_notice(&source, &m) - .context(FrippyErrorKind::Connection)?, - }, - Err(e) => { - let message = e.to_string(); - client - .send_notice(&source, &message) - .context(FrippyErrorKind::Connection)?; - Err(e).context(FrippyErrorKind::Factoids)? - } - } - - Ok(()) - } - - fn evaluate(&self, _: &Self::Client, _: PluginCommand) -> Result { - Err(String::from( - "Evaluation of commands is not implemented for Factoids at this time", - )) - } -} - -impl fmt::Debug for Factoids { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Factoids {{ ... }}") - } -} - -pub mod error { - #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)] - #[error = "FactoidsError"] - pub enum ErrorKind { - /// Invalid command error - #[fail(display = "Invalid Command")] - InvalidCommand, - - /// Invalid index error - #[fail(display = "Invalid index")] - InvalidIndex, - - /// Download error - #[fail(display = "Download failed")] - Download, - - /// Duplicate error - #[fail(display = "Entry already exists")] - Duplicate, - - /// Not found error - #[fail(display = "Factoid was not found")] - NotFound, - - /// MySQL error - #[cfg(feature = "mysql")] - #[fail(display = "Failed to execute MySQL Query")] - MysqlError, - - /// No connection error - #[cfg(feature = "mysql")] - #[fail(display = "No connection to the database")] - NoConnection, - } -} diff --git a/src/plugins/factoids/sandbox.lua b/src/plugins/factoids/sandbox.lua deleted file mode 100644 index a927535..0000000 --- a/src/plugins/factoids/sandbox.lua +++ /dev/null @@ -1,126 +0,0 @@ -function send(text) - local text = tostring(text) - local len = #output - if len < 1 then - output = { text } - else - output[len] = output[len] .. text - end -end - -function sendln(text) - send(text) - table.insert(output, "") -end - -function trim(s) - local from = s:match"^%s*()" - return from > #s and "" or s:match(".*%S", from) -end - -trimmedInput = trim(input) - -if trimmedInput == "" then - ioru = user -else - ioru = trimmedInput -end - -local sandbox_env = { - print = send, - println = sendln, - trim = trim, - eval = nil, - sleep = nil, - json = {decode = json_decode}, - args = args, - input = input, - user = user, - ioru = ioru, - channel = channel, - request = download, - string = string, - math = math, - table = table, - pairs = pairs, - ipairs = ipairs, - next = next, - select = select, - unpack = unpack, - tostring = tostring, - tonumber = tonumber, - type = type, - assert = assert, - error = error, - pcall = pcall, - xpcall = xpcall, - _VERSION = _VERSION -} - -sandbox_env.os = { - clock = os.clock, - time = os.time, - difftime = os.difftime -} - -sandbox_env.string.rep = nil -sandbox_env.string.dump = nil -sandbox_env.math.randomseed = nil - --- Temporary evaluation function -function eval(code) - local c, e = load(code, nil, nil, sandbox_env) - if c then - return c() - else - error(e) - end -end - --- Only sleeps for 1 second at a time --- This ensures that the timeout check can still run -function safesleep(dur) - while dur > 1000 do - dur = dur - 1000 - sleep(1000) - end - sleep(dur) -end - -sandbox_env.eval = eval -sandbox_env.sleep = safesleep - --- Check if the factoid timed out -function checktime() - if os.time() - time >= timeout then - error("Timed out after " .. timeout .. " seconds", 0) - else - -- Limit the cpu usage of factoids - sleep(1) - end -end - --- Check if the factoid uses too much memory -function checkmem() - if collectgarbage("count") > maxmem then - error("Factoid used over " .. maxmem .. " kbyte of ram") - end -end - -local f, e = load(factoid, nil, nil, sandbox_env) - --- Add timeout hook -time = os.time() --- The timeout is defined in seconds -timeout = 30 -debug.sethook(checktime, "l") --- Add memory check hook --- The max memory is defined in kilobytes -maxmem = 1000 -debug.sethook(checkmem, "l") - -if f then - f() -else - error(e) -end diff --git a/src/plugins/factoids/utils.rs b/src/plugins/factoids/utils.rs deleted file mode 100644 index a35dd27..0000000 --- a/src/plugins/factoids/utils.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::thread; -use std::time::Duration; - -use serde_json::{self, Value as SerdeValue}; - -use super::rlua::Error as LuaError; -use super::rlua::Error::RuntimeError; -use super::rlua::{Lua, Value as LuaValue}; - -use utils::error::ErrorKind::Connection; -use utils::Url; - -use failure::Fail; - -pub fn sleep(_: &Lua, dur: u64) -> Result<(), LuaError> { - thread::sleep(Duration::from_millis(dur)); - Ok(()) -} - -pub fn download(_: &Lua, url: String) -> Result { - let url = Url::from(url).max_kib(1024); - match url.request() { - Ok(v) => Ok(v), - Err(e) => { - let error = match e.kind() { - Connection => e.cause().unwrap().to_string(), - _ => e.to_string(), - }; - - Err(RuntimeError(format!( - "Failed to download {} - {}", - url.as_str(), - error - ))) - } - } -} - -fn convert_value(lua: &Lua, sval: SerdeValue, max_recurs: usize) -> Result { - if max_recurs == 0 { - return Err(RuntimeError(String::from( - "Reached max recursion level - json is nested too deep", - ))); - } - - let lval = match sval { - SerdeValue::Null => LuaValue::Nil, - SerdeValue::Bool(b) => LuaValue::Boolean(b), - SerdeValue::String(s) => LuaValue::String(lua.create_string(&s)?), - SerdeValue::Number(n) => { - let f = n.as_f64().ok_or_else(|| RuntimeError(String::from( - "Failed to convert number into double", - )))?; - LuaValue::Number(f) - } - SerdeValue::Array(arr) => { - let table = lua.create_table()?; - for (i, val) in arr.into_iter().enumerate() { - table.set(i + 1, convert_value(lua, val, max_recurs - 1)?)?; - } - - LuaValue::Table(table) - } - SerdeValue::Object(obj) => { - let table = lua.create_table()?; - for (key, val) in obj { - table.set(key, convert_value(lua, val, max_recurs - 1)?)?; - } - - LuaValue::Table(table) - } - }; - - Ok(lval) -} - -pub fn json_decode(lua: &Lua, json: String) -> Result { - let ser_val: SerdeValue = serde_json::from_str(&json).map_err(|e| RuntimeError(e.to_string()))?; - - convert_value(lua, ser_val, 25) -} diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index e3bda60..8aa19a0 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -1,9 +1,9 @@ //! Collection of plugins included pub mod emoji; -pub mod factoids; -pub mod quote; +pub mod factoid; pub mod help; pub mod keepnick; +pub mod quote; pub mod remind; pub mod sed; pub mod tell; -- cgit v1.2.3-70-g09d2 From 869b22874fb64935bff0f25f9a5dacb18b9d1548 Mon Sep 17 00:00:00 2001 From: Jokler Date: Wed, 20 Feb 2019 22:53:56 +0100 Subject: Plugins: Replace Emoji with Unicode The new plugin adds a command which allows checking the name and some info of a character. --- src/main.rs | 4 +- src/plugins/emoji.rs | 152 ------------------------------------------------- src/plugins/mod.rs | 2 +- src/plugins/unicode.rs | 100 ++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 155 deletions(-) delete mode 100644 src/plugins/emoji.rs create mode 100644 src/plugins/unicode.rs (limited to 'src/plugins/mod.rs') diff --git a/src/main.rs b/src/main.rs index 03f3c7e..ef24e4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,7 @@ use std::sync::Arc; use glob::glob; use irc::client::reactor::IrcReactor; -use frippy::plugins::emoji::Emoji; +use frippy::plugins::unicode::Unicode; use frippy::plugins::factoid::Factoid; use frippy::plugins::help::Help; use frippy::plugins::keepnick::KeepNick; @@ -108,7 +108,7 @@ fn run() -> Result<(), Error> { bot.add_plugin(Help::new()); bot.add_plugin(UrlTitles::new(1024)); bot.add_plugin(Sed::new(60)); - bot.add_plugin(Emoji::new()); + bot.add_plugin(Unicode::new()); bot.add_plugin(KeepNick::new()); #[cfg(feature = "mysql")] diff --git a/src/plugins/emoji.rs b/src/plugins/emoji.rs deleted file mode 100644 index aee61b1..0000000 --- a/src/plugins/emoji.rs +++ /dev/null @@ -1,152 +0,0 @@ -extern crate unicode_names; - -use std::fmt; -use std::marker::PhantomData; - -use irc::client::prelude::*; - -use plugin::*; -use FrippyClient; - -use error::ErrorKind as FrippyErrorKind; -use error::FrippyError; -use failure::Fail; -use failure::ResultExt; - -#[derive(Default, Debug)] -struct EmojiHandle { - symbol: char, - count: i32, -} - -impl fmt::Display for EmojiHandle { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let name = match unicode_names::name(self.symbol) { - Some(sym) => sym.to_string().to_lowercase(), - None => String::from("UNKNOWN"), - }; - - if self.count > 1 { - write!(f, "{}x {}", self.count, name) - } else { - write!(f, "{}", name) - } - } -} - -#[derive(PluginName, Default, Debug)] -pub struct Emoji { - phantom: PhantomData, -} - -impl Emoji { - pub fn new() -> Self { - Emoji { - phantom: PhantomData, - } - } - - fn emoji(&self, content: &str) -> Option { - let emojis = self.return_emojis(content); - if emojis.is_empty() { - None - } else { - Some( - emojis - .iter() - .map(|e| e.to_string()) - .collect::>() - .join(", "), - ) - } - } - - fn return_emojis(&self, string: &str) -> Vec { - let mut emojis: Vec = Vec::new(); - - let mut current = EmojiHandle::default(); - - for c in string.chars() { - if !self.is_emoji(c) { - continue; - } - - if current.symbol == c { - current.count += 1; - } else { - if current.count > 0 { - emojis.push(current); - } - - current = EmojiHandle { - symbol: c, - count: 1, - } - } - } - - if current.count > 0 { - emojis.push(current); - } - - emojis - } - - fn is_emoji(&self, c: char) -> bool { - // Emoji ranges from stackoverflow: - // https://stackoverflow.com/questions/30757193/find-out-if-character-in-string-is-emoji - match c { '\u{1F600}'...'\u{1F64F}' // Emoticons - | '\u{1F300}'...'\u{1F5FF}' // Misc Symbols and Pictographs - | '\u{1F680}'...'\u{1F6FF}' // Transport and Map - | '\u{2600}' ...'\u{26FF}' // Misc symbols - | '\u{2700}' ...'\u{27BF}' // Dingbats - | '\u{FE00}' ...'\u{FE0F}' // Variation Selectors - | '\u{1F900}'...'\u{1F9FF}' // Supplemental Symbols and Pictographs - | '\u{20D0}' ...'\u{20FF}' => true, // Combining Diacritical Marks for Symbols - _ => false, - } - } -} - -impl Plugin for Emoji { - type Client = C; - fn execute(&self, client: &Self::Client, message: &Message) -> ExecutionStatus { - match message.command { - Command::PRIVMSG(_, ref content) => { - if let Some(emojis) = self.emoji(content) { - match client.send_privmsg(message.response_target().unwrap(), &emojis) { - Ok(_) => ExecutionStatus::Done, - Err(e) => { - ExecutionStatus::Err(e.context(FrippyErrorKind::Connection).into()) - } - } - } else { - ExecutionStatus::Done - } - } - _ => ExecutionStatus::Done, - } - } - - fn execute_threaded(&self, _: &Self::Client, _: &Message) -> Result<(), FrippyError> { - panic!("Emoji should not use threading") - } - - fn command(&self, client: &Self::Client, command: PluginCommand) -> Result<(), FrippyError> { - client - .send_notice( - &command.source, - "This Plugin does not implement any commands.", - ).context(FrippyErrorKind::Connection)?; - - Ok(()) - } - - fn evaluate(&self, _: &Self::Client, command: PluginCommand) -> Result { - if let Some(emojis) = self.emoji(&command.tokens[0]) { - Ok(emojis) - } else { - Err(String::from("No emojis were found.")) - } - } -} diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 8aa19a0..0dfb011 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -1,5 +1,4 @@ //! Collection of plugins included -pub mod emoji; pub mod factoid; pub mod help; pub mod keepnick; @@ -7,4 +6,5 @@ pub mod quote; pub mod remind; pub mod sed; pub mod tell; +pub mod unicode; pub mod url; diff --git a/src/plugins/unicode.rs b/src/plugins/unicode.rs new file mode 100644 index 0000000..7633c2d --- /dev/null +++ b/src/plugins/unicode.rs @@ -0,0 +1,100 @@ +extern crate unicode_names; + +use std::marker::PhantomData; +use std::fmt; + +use irc::client::prelude::*; + +use plugin::*; +use FrippyClient; + +use error::ErrorKind as FrippyErrorKind; +use error::FrippyError; +use failure::Fail; +use failure::ResultExt; + +#[derive(PluginName, Default, Debug)] +pub struct Unicode { + phantom: PhantomData, +} + +impl Unicode { + pub fn new() -> Unicode { + Unicode { + phantom: PhantomData, + } + } + + fn get_name(&self, symbol: char) -> String { + match unicode_names::name(symbol) { + Some(sym) => sym.to_string().to_lowercase(), + None => String::from("UNKNOWN"), + } + } + + fn format_response(&self, content: &str) -> String { + let character = content.chars().next(); + if let Some(c) = character { + let mut buf = [0; 2]; + + let byte_string = c + .encode_utf8(&mut buf) + .as_bytes() + .iter() + .map(|b| format!("{:#b}", b)) + .collect::>() + .join(","); + + let name = self.get_name(c); + + format!( + "{} is '{}' | UTF-8: {2:#x} ({2}), Bytes: [{3}]", + c, name, c as u32, byte_string + ) + } else { + String::from("No non-space character was found.") + } + } +} + +impl Plugin for Unicode { + type Client = C; + + fn execute(&self, client: &Self::Client, message: &Message) -> ExecutionStatus { + ExecutionStatus::Done + } + + fn execute_threaded(&self, _: &Self::Client, _: &Message) -> Result<(), FrippyError> { + panic!("Unicode should not use threading") + } + + fn command(&self, client: &Self::Client, command: PluginCommand) -> Result<(), FrippyError> { + if command.tokens.is_empty() { + let msg = "No non-space character was found."; + + if let Err(e) = client.send_notice(command.source, msg) { + Err(e.context(FrippyErrorKind::Connection))?; + } + + return Ok(()); + } + + let content = &command.tokens[0]; + + if let Err(e) = client.send_privmsg(command.target, &self.format_response(&content)) { + Err(e.context(FrippyErrorKind::Connection))?; + } + + Ok(()) + } + + fn evaluate(&self, _: &Self::Client, command: PluginCommand) -> Result { + let tokens = command.tokens; + + if tokens.is_empty() { + return Err(String::from("No non-space character was found.")); + } + + Ok(self.format_response(&tokens[0])) + } +} -- cgit v1.2.3-70-g09d2