summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJokler <jokler.contact@gmail.com>2018-03-02 22:11:21 +0100
committerJokler <jokler.contact@gmail.com>2018-03-02 22:11:21 +0100
commit0b4131e8cf91ed10f24d3faed341034d518aea53 (patch)
tree09498ec2f2ec495a1b45a6762e61ed67f496c6f8 /src
parent0bcc7c0923852b48ebbb94ceeecc98f551fa920d (diff)
downloadfrippy-0b4131e8cf91ed10f24d3faed341034d518aea53.tar.gz
frippy-0b4131e8cf91ed10f24d3faed341034d518aea53.zip
Use Error & ErrorKind pair instead of simple enums
Each plugin should define its own errors with a respective variant in the main ErrorKind of frippy. A new procedural macro was added to reduce the boilerplate required for new error system. It can be used by deriving "Error" and adding a name for the Error via the "error" attribute. So far non of the plugins except for Url and Factoids use their own errors yet.
Diffstat (limited to 'src')
-rw-r--r--src/error.rs114
-rw-r--r--src/lib.rs56
-rw-r--r--src/main.rs51
-rw-r--r--src/plugin.rs21
-rw-r--r--src/plugins/currency.rs28
-rw-r--r--src/plugins/emoji.rs25
-rw-r--r--src/plugins/factoids/database.rs88
-rw-r--r--src/plugins/factoids/mod.rs187
-rw-r--r--src/plugins/factoids/utils.rs6
-rw-r--r--src/plugins/help.rs16
-rw-r--r--src/plugins/keepnick.rs29
-rw-r--r--src/plugins/mod.rs23
-rw-r--r--src/plugins/tell/mod.rs32
-rw-r--r--src/plugins/url.rs63
-rw-r--r--src/utils.rs34
15 files changed, 404 insertions, 369 deletions
diff --git a/src/error.rs b/src/error.rs
index fa232be..36d5724 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,101 +1,31 @@
//! Errors for `frippy` crate using `failure`.
-use std::io::Error as IoError;
-use irc::error::IrcError;
-use reqwest::Error as ReqwestError;
-#[cfg(feature = "mysql")]
-use r2d2::Error as R2d2Error;
+use failure::Fail;
-/// The main crate-wide error type.
-#[derive(Debug, Fail)]
-pub enum FrippyError {
- /// A plugin error
- #[fail(display = "A plugin error occured")]
- Plugin(#[cause] PluginError),
-
- /// An IRC error
- #[fail(display = "An IRC error occured")]
- Irc(#[cause] IrcError),
-
- /// Missing config error
- #[fail(display = "No config file was found")]
- MissingConfig,
-
- /// A reqwest error
- #[fail(display = "A reqwest error occured")]
- Reqwest(#[cause] ReqwestError),
-
- /// An I/O error
- #[fail(display = "An I/O error occured")]
- Io(#[cause] IoError),
-
- /// An r2d2 error
- #[cfg(feature = "mysql")]
- #[fail(display = "An r2d2 error occured")]
- R2d2(#[cause] R2d2Error),
-
- /// Reached download limit error
- #[fail(display = "Reached download limit of {} KiB", limit)]
- DownloadLimit { limit: usize },
+pub fn log_error(e: FrippyError) {
+ let text = e.causes()
+ .skip(1)
+ .fold(format!("{}", e), |acc, err| format!("{}: {}", acc, err));
+ error!("{}", text);
}
-/// Errors related to plugins
-#[derive(Debug, Fail)]
-pub enum PluginError {
- /// A Url error
- #[fail(display = "A Url error occured")]
- Url(#[cause] UrlError),
-
- /// A Factoids error
- #[fail(display = "{}", error)]
- Factoids { error: String },
-}
-
-/// A URL plugin error
-#[derive(Debug, Fail)]
-pub enum UrlError {
- /// Missing URL error
- #[fail(display = "No URL was found")]
- MissingUrl,
-
- /// Missing title error
- #[fail(display = "No title was found")]
- MissingTitle,
-}
-
-impl From<UrlError> for FrippyError {
- fn from(e: UrlError) -> FrippyError {
- FrippyError::Plugin(PluginError::Url(e))
- }
-}
-
-impl From<PluginError> for FrippyError {
- fn from(e: PluginError) -> FrippyError {
- FrippyError::Plugin(e)
- }
-}
-
-impl From<IrcError> for FrippyError {
- fn from(e: IrcError) -> FrippyError {
- FrippyError::Irc(e)
- }
-}
+/// The main crate-wide error type.
+#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
+#[error = "FrippyError"]
+pub enum ErrorKind {
+ /// Connection error
+ #[fail(display = "A connection error occured")]
+ Connection,
-impl From<ReqwestError> for FrippyError {
- fn from(e: ReqwestError) -> FrippyError {
- FrippyError::Reqwest(e)
- }
-}
+ /// A Url error
+ #[fail(display = "A Url error has occured")]
+ Url,
-impl From<IoError> for FrippyError {
- fn from(e: IoError) -> FrippyError {
- FrippyError::Io(e)
- }
-}
+ /// A Tell error
+ #[fail(display = "A Tell error has occured")]
+ Tell,
-#[cfg(feature = "mysql")]
-impl From<R2d2Error> for FrippyError {
- fn from(e: R2d2Error) -> FrippyError {
- FrippyError::R2d2(e)
- }
+ /// A Factoids error
+ #[fail(display = "A Factoids error has occured")]
+ Factoids,
}
diff --git a/src/lib.rs b/src/lib.rs
index 42b0089..8cf1e2f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -64,7 +64,8 @@ use std::sync::Arc;
pub use irc::client::prelude::*;
pub use irc::error::IrcError;
-use error::FrippyError;
+use error::*;
+use failure::ResultExt;
use plugin::*;
@@ -150,11 +151,13 @@ impl Bot {
pub fn connect(&self, reactor: &mut IrcReactor, config: &Config) -> Result<(), FrippyError> {
info!("Plugins loaded: {}", self.plugins);
- let client = reactor.prepare_client_and_connect(config)?;
+ let client = reactor
+ .prepare_client_and_connect(config)
+ .context(ErrorKind::Connection)?;
info!("Connected to IRC server");
- client.identify()?;
+ client.identify().context(ErrorKind::Connection)?;
info!("Identified");
// TODO Verify if we actually need to clone plugins twice
@@ -169,25 +172,25 @@ impl Bot {
}
fn process_msg(
- server: &IrcClient,
+ client: &IrcClient,
mut plugins: ThreadedPlugins,
message: Message,
) -> Result<(), IrcError> {
// Log any channels we join
if let Command::JOIN(ref channel, _, _) = message.command {
- if message.source_nickname().unwrap() == server.current_nickname() {
+ if message.source_nickname().unwrap() == client.current_nickname() {
info!("Joined {}", channel);
}
}
// Check for possible command and save the result for later
- let command = PluginCommand::from(&server.current_nickname().to_lowercase(), &message);
+ let command = PluginCommand::from(&client.current_nickname().to_lowercase(), &message);
- plugins.execute_plugins(server, message);
+ plugins.execute_plugins(client, message);
// If the message contained a command, handle it
if let Some(command) = command {
- if let Err(e) = plugins.handle_command(server, command) {
+ if let Err(e) = plugins.handle_command(client, command) {
error!("Failed to handle command: {}", e);
}
}
@@ -218,12 +221,12 @@ impl ThreadedPlugins {
self.plugins.remove(&name.to_lowercase()).map(|_| ())
}
- pub fn execute_plugins(&mut self, server: &IrcClient, message: Message) {
+ pub fn execute_plugins(&mut self, client: &IrcClient, message: Message) {
let message = Arc::new(message);
for (name, plugin) in self.plugins.clone() {
// Send the message to the plugin if the plugin needs it
- match plugin.execute(server, &message) {
+ match plugin.execute(client, &message) {
ExecutionStatus::Done => (),
ExecutionStatus::Err(e) => error!("Error in {} - {}", name, e),
ExecutionStatus::RequiresThread => {
@@ -233,15 +236,15 @@ impl ThreadedPlugins {
message.to_string().replace("\r\n", "")
);
- // Clone everything before the move - the server uses an Arc internally too
+ // Clone everything before the move - the client uses an Arc internally too
let plugin = Arc::clone(&plugin);
let message = Arc::clone(&message);
- let server = server.clone();
+ let client = client.clone();
// Execute the plugin in another thread
spawn(move || {
- if let Err(e) = plugin.execute_threaded(&server, &message) {
- error!("Error in {} - {}", name, e);
+ if let Err(e) = plugin.execute_threaded(&client, &message) {
+ log_error(e);
};
});
}
@@ -251,12 +254,14 @@ impl ThreadedPlugins {
pub fn handle_command(
&mut self,
- server: &IrcClient,
+ client: &IrcClient,
mut command: PluginCommand,
) -> Result<(), FrippyError> {
if !command.tokens.iter().any(|s| !s.is_empty()) {
- let help = format!("Use \"{} help\" to get help", server.current_nickname());
- server.send_notice(&command.source, &help)?;
+ let help = format!("Use \"{} help\" to get help", client.current_nickname());
+ client
+ .send_notice(&command.source, &help)
+ .context(ErrorKind::Connection)?;
}
// Check if the command is for this plugin
@@ -266,12 +271,12 @@ impl ThreadedPlugins {
debug!("Sending command \"{:?}\" to {}", command, name);
- // Clone for the move - the server uses an Arc internally
- let server = server.clone();
+ // Clone for the move - the client uses an Arc internally
+ let client = client.clone();
let plugin = Arc::clone(plugin);
spawn(move || {
- if let Err(e) = plugin.command(&server, command) {
- error!("Error in {} command - {}", name, e);
+ if let Err(e) = plugin.command(&client, command) {
+ log_error(e);
};
});
@@ -280,11 +285,13 @@ impl ThreadedPlugins {
let help = format!(
"\"{} {}\" is not a command, \
try \"{0} help\" instead.",
- server.current_nickname(),
+ client.current_nickname(),
command.tokens[0]
);
- Ok(server.send_notice(&command.source, &help)?)
+ Ok(client
+ .send_notice(&command.source, &help)
+ .context(ErrorKind::Connection)?)
}
}
}
@@ -298,6 +305,3 @@ impl fmt::Display for ThreadedPlugins {
write!(f, "{}", plugin_names.join(", "))
}
}
-
-#[cfg(test)]
-mod tests {}
diff --git a/src/main.rs b/src/main.rs
index 3432e3e..b9a4b8f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,6 +17,8 @@ extern crate r2d2;
extern crate r2d2_diesel;
#[macro_use]
+extern crate failure;
+#[macro_use]
extern crate log;
#[cfg(feature = "mysql")]
@@ -27,9 +29,16 @@ use log::{Level, LevelFilter, Metadata, Record};
use irc::client::reactor::IrcReactor;
use glob::glob;
-use frippy::plugins;
+pub use frippy::plugins::help::Help;
+pub use frippy::plugins::url::Url;
+pub use frippy::plugins::emoji::Emoji;
+pub use frippy::plugins::tell::Tell;
+pub use frippy::plugins::currency::Currency;
+pub use frippy::plugins::keepnick::KeepNick;
+pub use frippy::plugins::factoids::Factoids;
+
use frippy::Config;
-use frippy::error::FrippyError;
+use failure::Error;
#[cfg(feature = "mysql")]
embed_migrations!();
@@ -70,11 +79,14 @@ static LOGGER: Logger = Logger;
fn main() {
// Print any errors that caused frippy to shut down
if let Err(e) = run() {
- frippy::utils::log_error(e);
+ let text = e.causes()
+ .skip(1)
+ .fold(format!("{}", e), |acc, err| format!("{}: {}", acc, err));
+ error!("{}", text);
};
}
-fn run() -> Result<(), FrippyError> {
+fn run() -> Result<(), Error> {
log::set_max_level(if cfg!(debug_assertions) {
LevelFilter::Debug
} else {
@@ -100,7 +112,7 @@ fn run() -> Result<(), FrippyError> {
// Without configs the bot would just idle
if configs.is_empty() {
- return Err(FrippyError::MissingConfig);
+ bail!("No config file was found");
}
// Create an event loop to run the connections on.
@@ -119,11 +131,11 @@ fn run() -> Result<(), FrippyError> {
}
let mut bot = frippy::Bot::new();
- bot.add_plugin(plugins::Help::new());
- bot.add_plugin(plugins::Url::new(1024));
- bot.add_plugin(plugins::Emoji::new());
- bot.add_plugin(plugins::Currency::new());
- bot.add_plugin(plugins::KeepNick::new());
+ bot.add_plugin(Help::new());
+ bot.add_plugin(Url::new(1024));
+ bot.add_plugin(Emoji::new());
+ bot.add_plugin(Currency::new());
+ bot.add_plugin(KeepNick::new());
#[cfg(feature = "mysql")]
{
@@ -134,25 +146,24 @@ fn run() -> Result<(), FrippyError> {
let manager = ConnectionManager::<MysqlConnection>::new(url.clone());
match r2d2::Pool::builder().build(manager) {
- Ok(pool) => match embedded_migrations::run(&*pool.get()?)
- {
+ Ok(pool) => match embedded_migrations::run(&*pool.get()?) {
Ok(_) => {
let pool = Arc::new(pool);
- bot.add_plugin(plugins::Factoids::new(pool.clone()));
- bot.add_plugin(plugins::Tell::new(pool.clone()));
+ bot.add_plugin(Factoids::new(pool.clone()));
+ bot.add_plugin(Tell::new(pool.clone()));
info!("Connected to MySQL server")
}
Err(e) => {
- bot.add_plugin(plugins::Factoids::new(HashMap::new()));
- bot.add_plugin(plugins::Tell::new(HashMap::new()));
+ bot.add_plugin(Factoids::new(HashMap::new()));
+ bot.add_plugin(Tell::new(HashMap::new()));
error!("Failed to run migrations: {}", e);
}
},
Err(e) => error!("Failed to connect to database: {}", e),
}
} else {
- bot.add_plugin(plugins::Factoids::new(HashMap::new()));
- bot.add_plugin(plugins::Tell::new(HashMap::new()));
+ bot.add_plugin(Factoids::new(HashMap::new()));
+ bot.add_plugin(Tell::new(HashMap::new()));
}
}
#[cfg(not(feature = "mysql"))]
@@ -160,8 +171,8 @@ fn run() -> Result<(), FrippyError> {
if mysql_url.is_some() {
error!("frippy was not built with the mysql feature")
}
- bot.add_plugin(plugins::Factoids::new(HashMap::new()));
- bot.add_plugin(plugins::Tell::new(HashMap::new()));
+ bot.add_plugin(Factoids::new(HashMap::new()));
+ bot.add_plugin(Tell::new(HashMap::new()));
}
if let Some(disabled_plugins) = disabled_plugins {
diff --git a/src/plugin.rs b/src/plugin.rs
index f33fa80..e57f072 100644
--- a/src/plugin.rs
+++ b/src/plugin.rs
@@ -2,16 +2,17 @@
use std::fmt;
use irc::client::prelude::*;
-use irc::error::IrcError;
+use error::FrippyError;
/// 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.
#[derive(Debug)]
pub enum ExecutionStatus {
- /// The [`Plugin`](trait.Plugin.html) does not need to do any more work on this [`Message`](../../irc/proto/message/struct.Message.html).
+ /// The [`Plugin`](trait.Plugin.html) does not need to do any more work on this
+ /// [`Message`](../../irc/proto/message/struct.Message.html).
Done,
/// An error occured during the execution.
- Err(Box<IrcError>),
+ Err(FrippyError),
/// The execution needs to be done by [`execute_threaded()`](trait.Plugin.html#tymethod.execute_threaded).
RequiresThread,
}
@@ -19,15 +20,17 @@ pub enum ExecutionStatus {
/// `Plugin` has to be implemented for any struct that should be usable
/// as a `Plugin` in frippy.
pub trait Plugin: PluginName + Send + Sync + fmt::Debug {
- /// Handles messages which are not commands or returns [`RequiresThread`](enum.ExecutionStatus.html#variant.RequiresThread)
+ /// Handles messages which are not commands or returns
+ /// [`RequiresThread`](enum.ExecutionStatus.html#variant.RequiresThread)
/// if [`execute_threaded()`](trait.Plugin.html#tymethod.execute_threaded) should be used instead.
- fn execute(&self, server: &IrcClient, message: &Message) -> ExecutionStatus;
+ fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus;
/// Handles messages which are not commands in a new thread.
- fn execute_threaded(&self, server: &IrcClient, message: &Message) -> Result<(), IrcError>;
+ fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), FrippyError>;
/// Handles any command directed at this plugin.
- fn command(&self, server: &IrcClient, command: PluginCommand) -> Result<(), IrcError>;
- /// Similar to [`command()`](trait.Plugin.html#tymethod.command) but return a String instead of sending messages directly to IRC.
- fn evaluate(&self, server: &IrcClient, command: PluginCommand) -> Result<String, String>;
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError>;
+ /// Similar to [`command()`](trait.Plugin.html#tymethod.command) but return a String instead of
+ /// sending messages directly to IRC.
+ fn evaluate(&self, client: &IrcClient, command: PluginCommand) -> Result<String, String>;
}
/// `PluginName` is required by [`Plugin`](trait.Plugin.html).
diff --git a/src/plugins/currency.rs b/src/plugins/currency.rs
index 958c8e2..53a245c 100644
--- a/src/plugins/currency.rs
+++ b/src/plugins/currency.rs
@@ -6,7 +6,6 @@ use std::io::Read;
use std::num::ParseFloatError;
use irc::client::prelude::*;
-use irc::error::IrcError;
use self::reqwest::Client;
use self::reqwest::header::Connection;
@@ -14,6 +13,10 @@ use self::serde_json::Value;
use plugin::*;
+use error::FrippyError;
+use error::ErrorKind as FrippyErrorKind;
+use failure::ResultExt;
+
#[derive(PluginName, Default, Debug)]
pub struct Currency;
@@ -124,20 +127,28 @@ impl Plugin for Currency {
ExecutionStatus::Done
}
- fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), IrcError> {
+ 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<(), IrcError> {
+ fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), FrippyError> {
if command.tokens.is_empty() {
- return client.send_notice(&command.source, &self.invalid_command(client));
+ return Ok(client
+ .send_notice(&command.source, &self.invalid_command(client))
+ .context(FrippyErrorKind::Connection)?);
}
match command.tokens[0].as_ref() {
- "help" => client.send_notice(&command.source, &self.help(client)),
+ "help" => Ok(client
+ .send_notice(&command.source, &self.help(client))
+ .context(FrippyErrorKind::Connection)?),
_ => match self.convert(client, &mut command) {
- Ok(msg) => client.send_privmsg(&command.target, &msg),
- Err(msg) => client.send_notice(&command.source, &msg),
+ Ok(msg) => Ok(client
+ .send_privmsg(&command.target, &msg)
+ .context(FrippyErrorKind::Connection)?),
+ Err(msg) => Ok(client
+ .send_notice(&command.source, &msg)
+ .context(FrippyErrorKind::Connection)?),
},
}
}
@@ -153,6 +164,3 @@ impl Plugin for Currency {
}
}
}
-
-#[cfg(test)]
-mod tests {}
diff --git a/src/plugins/emoji.rs b/src/plugins/emoji.rs
index a4276f4..f1d9376 100644
--- a/src/plugins/emoji.rs
+++ b/src/plugins/emoji.rs
@@ -3,10 +3,14 @@ extern crate unicode_names;
use std::fmt;
use irc::client::prelude::*;
-use irc::error::IrcError;
use plugin::*;
+use error::FrippyError;
+use error::ErrorKind as FrippyErrorKind;
+use failure::Fail;
+use failure::ResultExt;
+
struct EmojiHandle {
symbol: char,
count: i32,
@@ -100,21 +104,23 @@ impl Plugin for Emoji {
.send_privmsg(message.response_target().unwrap(), &self.emoji(content))
{
Ok(_) => ExecutionStatus::Done,
- Err(e) => ExecutionStatus::Err(Box::new(e)),
+ Err(e) => ExecutionStatus::Err(e.context(FrippyErrorKind::Connection).into()),
},
_ => ExecutionStatus::Done,
}
}
- fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), IrcError> {
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
panic!("Emoji should not use threading")
}
- fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), IrcError> {
- client.send_notice(
- &command.source,
- "This Plugin does not implement any commands.",
- )
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
+ Ok(client
+ .send_notice(
+ &command.source,
+ "This Plugin does not implement any commands.",
+ )
+ .context(FrippyErrorKind::Connection)?)
}
fn evaluate(&self, _: &IrcClient, command: PluginCommand) -> Result<String, String> {
@@ -126,6 +132,3 @@ impl Plugin for Emoji {
}
}
}
-
-#[cfg(test)]
-mod tests {}
diff --git a/src/plugins/factoids/database.rs b/src/plugins/factoids/database.rs
index 88aa0fd..b1fe8dd 100644
--- a/src/plugins/factoids/database.rs
+++ b/src/plugins/factoids/database.rs
@@ -13,13 +13,12 @@ use diesel::mysql::MysqlConnection;
use r2d2::Pool;
#[cfg(feature = "mysql")]
use r2d2_diesel::ConnectionManager;
+#[cfg(feature = "mysql")]
+use failure::ResultExt;
use chrono::NaiveDateTime;
-pub enum DbResponse {
- Success,
- Failed(&'static str),
-}
+use super::error::*;
#[cfg_attr(feature = "mysql", derive(Queryable))]
#[derive(Clone, Debug)]
@@ -42,15 +41,15 @@ pub struct NewFactoid<'a> {
}
pub trait Database: Send {
- fn insert_factoid(&mut self, factoid: &NewFactoid) -> DbResponse;
- fn get_factoid(&self, name: &str, idx: i32) -> Option<Factoid>;
- fn delete_factoid(&mut self, name: &str, idx: i32) -> DbResponse;
- fn count_factoids(&self, name: &str) -> Result<i32, &'static str>;
+ fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidsError>;
+ fn get_factoid(&self, name: &str, idx: i32) -> Result<Factoid, FactoidsError>;
+ fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError>;
+ fn count_factoids(&self, name: &str) -> Result<i32, FactoidsError>;
}
// HashMap
impl Database for HashMap<(String, i32), Factoid> {
- fn insert_factoid(&mut self, factoid: &NewFactoid) -> DbResponse {
+ fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidsError> {
let factoid = Factoid {
name: String::from(factoid.name),
idx: factoid.idx,
@@ -61,23 +60,25 @@ impl Database for HashMap<(String, i32), Factoid> {
let name = factoid.name.clone();
match self.insert((name, factoid.idx), factoid) {
- None => DbResponse::Success,
- Some(_) => DbResponse::Failed("Factoid was overwritten"),
+ None => Ok(()),
+ Some(_) => Err(ErrorKind::Duplicate)?,
}
}
- fn get_factoid(&self, name: &str, idx: i32) -> Option<Factoid> {
- self.get(&(String::from(name), idx)).cloned()
+ fn get_factoid(&self, name: &str, idx: i32) -> Result<Factoid, FactoidsError> {
+ Ok(self.get(&(String::from(name), idx))
+ .cloned()
+ .ok_or(ErrorKind::NotFound)?)
}
- fn delete_factoid(&mut self, name: &str, idx: i32) -> DbResponse {
+ fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError> {
match self.remove(&(String::from(name), idx)) {
- Some(_) => DbResponse::Success,
- None => DbResponse::Failed("Factoid not found"),
+ Some(_) => Ok(()),
+ None => Err(ErrorKind::NotFound)?,
}
}
- fn count_factoids(&self, name: &str) -> Result<i32, &'static str> {
+ fn count_factoids(&self, name: &str) -> Result<i32, FactoidsError> {
Ok(self.iter().filter(|&(&(ref n, _), _)| n == name).count() as i32)
}
}
@@ -102,38 +103,31 @@ use self::schema::factoids;
#[cfg(feature = "mysql")]
impl Database for Arc<Pool<ConnectionManager<MysqlConnection>>> {
- fn insert_factoid(&mut self, factoid: &NewFactoid) -> DbResponse {
+ fn insert_factoid(&mut self, factoid: &NewFactoid) -> Result<(), FactoidsError> {
use diesel;
- let conn = &*self.get().expect("Failed to get connection");
- match diesel::insert_into(factoids::table)
+ let conn = &*self.get().context(ErrorKind::NoConnection)?;
+ diesel::insert_into(factoids::table)
.values(factoid)
.execute(conn)
- {
- Ok(_) => DbResponse::Success,
- Err(e) => {
- error!("DB Insertion Error: {}", e);
- DbResponse::Failed("Failed to add factoid")
- }
- }
+ .context(ErrorKind::MysqlError)?;
+
+ Ok(())
}
- fn get_factoid(&self, name: &str, idx: i32) -> Option<Factoid> {
- let conn = &*self.get().expect("Failed to get connection");
- match factoids::table.find((name, idx)).first(conn) {
- Ok(f) => Some(f),
- Err(e) => {
- error!("DB Count Error: {}", e);
- None
- }
- }
+ fn get_factoid(&self, name: &str, idx: i32) -> Result<Factoid, FactoidsError> {
+ 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) -> DbResponse {
+ fn delete_factoid(&mut self, name: &str, idx: i32) -> Result<(), FactoidsError> {
use diesel;
use self::factoids::columns;
- let conn = &*self.get().expect("Failed to get connection");
+ let conn = &*self.get().context(ErrorKind::NoConnection)?;
match diesel::delete(
factoids::table
.filter(columns::name.eq(name))
@@ -142,22 +136,19 @@ impl Database for Arc<Pool<ConnectionManager<MysqlConnection>>> {
{
Ok(v) => {
if v > 0 {
- DbResponse::Success
+ Ok(())
} else {
- DbResponse::Failed("Could not find any factoid with that name")
+ Err(ErrorKind::NotFound)?
}
}
- Err(e) => {
- error!("DB Deletion Error: {}", e);
- DbResponse::Failed("Failed to delete factoid")
- }
+ Err(e) => Err(e).context(ErrorKind::MysqlError)?,
}
}
- fn count_factoids(&self, name: &str) -> Result<i32, &'static str> {
+ fn count_factoids(&self, name: &str) -> Result<i32, FactoidsError> {
use diesel;
- let conn = &*self.get().expect("Failed to get connection");
+ let conn = &*self.get().context(ErrorKind::NoConnection)?;
let count: Result<i64, _> = factoids::table
.filter(factoids::columns::name.eq(name))
.count()
@@ -166,10 +157,7 @@ impl Database for Arc<Pool<ConnectionManager<MysqlConnection>>> {
match count {
Ok(c) => Ok(c as i32),
Err(diesel::NotFound) => Ok(0),
- Err(e) => {
- error!("DB Count Error: {}", e);
- Err("Database Error")
- }
+ Err(e) => Err(e).context(ErrorKind::MysqlError)?,
}
}
}
diff --git a/src/plugins/factoids/mod.rs b/src/plugins/factoids/mod.rs
index 806bb7e..2f3690f 100644
--- a/src/plugins/factoids/mod.rs
+++ b/src/plugins/factoids/mod.rs
@@ -5,21 +5,22 @@ use std::str::FromStr;
use std::sync::Mutex;
use self::rlua::prelude::*;
use irc::client::prelude::*;
-use irc::error::IrcError;
-use error::FrippyError;
-use error::PluginError;
-use failure::Fail;
use time;
use chrono::NaiveDateTime;
use plugin::*;
pub mod database;
-use self::database::{Database, DbResponse};
+use self::database::Database;
mod utils;
use self::utils::*;
+use failure::ResultExt;
+use error::ErrorKind as FrippyErrorKind;
+use error::FrippyError;
+use self::error::*;
+
static LUA_SANDBOX: &'static str = include_str!("sandbox.lua");
#[derive(PluginName)]
@@ -43,8 +44,13 @@ impl<T: Database> Factoids<T> {
}
}
- fn create_factoid(&self, name: &str, content: &str, author: &str) -> Result<&str, FrippyError> {
- let count = try_lock!(self.factoids).count_factoids(name).map_err(|e| PluginError::Factoids { error: e.to_owned() })?;
+ fn create_factoid(
+ &self,
+ name: &str,
+ content: &str,
+ author: &str,
+ ) -> Result<&str, FactoidsError> {
+ let count = try_lock!(self.factoids).count_factoids(name)?;
let tm = time::now().to_timespec();
let factoid = database::NewFactoid {
@@ -55,15 +61,14 @@ impl<T: Database> Factoids<T> {
created: NaiveDateTime::from_timestamp(tm.sec, 0u32),
};
- match try_lock!(self.factoids).insert_factoid(&factoid) {
- DbResponse::Success => Ok("Successfully added"),
- DbResponse::Failed(e) => Err(PluginError::Factoids { error: e.to_owned() })?,
- }
+ Ok(try_lock!(self.factoids)
+ .insert_factoid(&factoid)
+ .map(|()| "Successfully added!")?)
}
- fn add(&self, client: &IrcClient, command: &mut PluginCommand) -> Result<&str, FrippyError> {
+ fn add(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> {
if command.tokens.len() < 2 {
- return Ok(self.invalid_command(client, command).map(|()| "")?);
+ Err(ErrorKind::InvalidCommand)?;
}
let name = command.tokens.remove(0);
@@ -72,45 +77,41 @@ impl<T: Database> Factoids<T> {
Ok(self.create_factoid(&name, &content, &command.source)?)
}
- fn add_from_url(
- &self,
- client: &IrcClient,
- command: &mut PluginCommand,
- ) -> Result<&str, FrippyError> {
+ fn add_from_url(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> {
if command.tokens.len() < 2 {
- return Ok(self.invalid_command(client, command).map(|()| "")?);
+ Err(ErrorKind::InvalidCommand)?;
}
let name = command.tokens.remove(0);
let url = &command.tokens[0];
- let content = ::utils::download(url, Some(1024))?;
+ let content = ::utils::download(url, Some(1024)).context(ErrorKind::Download)?;
Ok(self.create_factoid(&name, &content, &command.source)?)
}
- fn remove(&self, client: &IrcClient, command: &mut PluginCommand) -> Result<&str, FrippyError> {
+ fn remove(&self, command: &mut PluginCommand) -> Result<&str, FactoidsError> {
if command.tokens.len() < 1 {
- return Ok(self.invalid_command(client, command).map(|()| "")?);
+ Err(ErrorKind::InvalidCommand)?;
}
let name = command.tokens.remove(0);
- let count = try_lock!(self.factoids).count_factoids(&name).map_err(|e| PluginError::Factoids { error: e.to_owned() } )?;
+ let count = try_lock!(self.factoids).count_factoids(&name)?;
match try_lock!(self.factoids).delete_factoid(&name, count - 1) {
- DbResponse::Success => Ok("Successfully removed"),
- DbResponse::Failed(e) => Err(PluginError::Factoids { error: e.to_owned() })?,
+ Ok(()) => Ok("Successfully removed"),
+ Err(e) => Err(e)?,
}
}
- fn get(&self, client: &IrcClient, command: &PluginCommand) -> Result<String, FrippyError> {
+ fn get(&self, command: &PluginCommand) -> Result<String, FactoidsError> {
let (name, idx) = match command.tokens.len() {
- 0 => return Ok(self.invalid_command(client, command).map(|()| String::new())?),
+ 0 => Err(ErrorKind::InvalidCommand)?,
1 => {
let name = &command.tokens[0];
- let count = try_lock!(self.factoids).count_factoids(name).map_err(|e| PluginError::Factoids { error: e.to_owned() } )?;
+ let count = try_lock!(self.factoids).count_factoids(name)?;
if count < 1 {
- Err(PluginError::Factoids { error: format!("{} does not exist", name) })?;
+ Err(ErrorKind::NotFound)?;
}
(name, count - 1)
@@ -119,61 +120,55 @@ impl<T: Database> Factoids<T> {
let name = &command.tokens[0];
let idx = match i32::from_str(&command.tokens[1]) {
Ok(i) => i,
- Err(_) => Err(PluginError::Factoids { error: String::from("Invalid index") })?,
+ Err(_) => Err(ErrorKind::InvalidCommand)?,
};
(name, idx)
}
};
- let factoid = match try_lock!(self.factoids).get_factoid(name, idx) {
- Some(v) => v,
- None => Err(PluginError::Factoids { error: format!("{}~{} does not exist", name, idx) })?,
- };
+ let factoid = try_lock!(self.factoids)
+ .get_factoid(name, idx)
+ .context(ErrorKind::NotFound)?;
let message = factoid.content.replace("\n", "|").replace("\r", "");
Ok(format!("{}: {}", factoid.name, message))
}
- fn info(&self, client: &IrcClient, command: &PluginCommand) -> Result<String, FrippyError> {
+ fn info(&self, command: &PluginCommand) -> Result<String, FactoidsError> {
match command.tokens.len() {
- 0 => Ok(self.invalid_command(client, command).map(|()| String::new())?),
+ 0 => Err(ErrorKind::InvalidCommand)?,
1 => {
let name = &command.tokens[0];
- let count = try_lock!(self.factoids).count_factoids(name).map_err(|e| PluginError::Factoids { error: e.to_owned() } )?;
+ let count = try_lock!(self.factoids).count_factoids(name)?;
Ok(match count {
- 0 => Err(PluginError::Factoids { error: format!("{} does not exist", name) })?,
+ 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]).map_err(|_| PluginError::Factoids { error: String::from("Invalid index") })?;
-
- let factoid = match try_lock!(self.factoids).get_factoid(name, idx) {
- Some(v) => v,
- None => return Ok(format!("{}~{} does not exist", name, idx)),
- };
+ let idx = i32::from_str(&command.tokens[1]).context(ErrorKind::InvalidIndex)?;
+ let factoid = try_lock!(self.factoids).get_factoid(name, idx)?;
- Ok(format!("{}: Added by {} at {} UTC", name, factoid.author, factoid.created))
+ Ok(format!(
+ "{}: Added by {} at {} UTC",
+ name, factoid.author, factoid.created
+ ))
}
}
}
- fn exec(
- &self,
- client: &IrcClient,
- mut command: PluginCommand,
- ) -> Result<String, FrippyError> {
+ fn exec(&self, mut command: PluginCommand) -> Result<String, FactoidsError> {
if command.tokens.len() < 1 {
- Ok(self.invalid_command(client, &command).map(|()| String::new())?)
+ Err(ErrorKind::InvalidIndex)?
} else {
let name = command.tokens.remove(0);
- let count = try_lock!(self.factoids).count_factoids(&name).map_err(|e| PluginError::Factoids { error: e.to_owned() } )?;
- let factoid = try_lock!(self.factoids).get_factoid(&name, count - 1).ok_or(PluginError::Factoids { error: format!("The factoid \"{}\" does not exist", name) })?;
+ let count = try_lock!(self.factoids).count_factoids(&name)?;
+ let factoid = try_lock!(self.factoids).get_factoid(&name, count - 1)?;
let content = factoid.content;
let value = if content.starts_with('>') {
@@ -225,10 +220,6 @@ impl<T: Database> Factoids<T> {
Ok(output.join("|"))
}
-
- fn invalid_command(&self, client: &IrcClient, command: &PluginCommand) -> Result<(), IrcError> {
- client.send_notice(&command.source, "Invalid Command")
- }
}
impl<T: Database> Plugin for Factoids<T> {
@@ -243,7 +234,7 @@ impl<T: Database> Plugin for Factoids<T> {
}
}
- fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), IrcError> {
+ fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), FrippyError> {
if let Command::PRIVMSG(_, mut content) = message.command.clone() {
content.remove(0);
@@ -255,18 +246,22 @@ impl<T: Database> Plugin for Factoids<T> {
tokens: t,
};
- match self.exec(client, c) {
- Ok(f) => client.send_privmsg(&message.response_target().unwrap(), &f),
- Err(_) => Ok(()),
- }
+ Ok(match self.exec(c) {
+ Ok(f) => client
+ .send_privmsg(&message.response_target().unwrap(), &f)
+ .context(FrippyErrorKind::Connection)?,
+ Err(_) => (),
+ })
} else {
Ok(())
}
}
- fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), IrcError> {
+ fn command(&self, client: &IrcClient, mut command: PluginCommand) -> Result<(), FrippyError> {
if command.tokens.is_empty() {
- return self.invalid_command(client, &command);
+ return Ok(client
+ .send_notice(&command.target, "Invalid command")
+ .context(FrippyErrorKind::Connection)?);
}
let target = command.target.clone();
@@ -274,19 +269,27 @@ impl<T: Database> Plugin for Factoids<T> {
let sub_command = command.tokens.remove(0);
let result = match sub_command.as_ref() {
- "add" => self.add(client, &mut command).map(|s| s.to_owned()),
- "fromurl" => self.add_from_url(client, &mut command).map(|s| s.to_owned()),
- "remove" => self.remove(client, &mut command).map(|s| s.to_owned()),
- "get" => self.get(client, &command),
- "info" => self.info(client, &command),
- "exec" => self.exec(client, command),
- _ => self.invalid_command(client, &command).map(|()| String::new()).map_err(|e| e.into()),
+ "add" => self.add(&mut command).map(|s| s.to_owned()),
+ "fromurl" => self.add_from_url(&mut command).map(|s| s.to_owned()),
+ "remove" => self.remove(&mut command).map(|s| s.to_owned()),
+ "get" => self.get(&command),
+ "info" => self.info(&command),
+ "exec" => self.exec(command),
+ _ => Err(ErrorKind::InvalidCommand.into()),
};
Ok(match result {
- Ok(v) => client.send_privmsg(&target, &v),
- Err(e) => client.send_notice(&source, &e.cause().unwrap().to_string()),
- }?)
+ Ok(v) => client
+ .send_privmsg(&target, &v)
+ .context(FrippyErrorKind::Connection)?,
+ Err(e) => {
+ let message = e.to_string();
+ client
+ .send_notice(&source, &message)
+ .context(FrippyErrorKind::Connection)?;
+ Err(e).context(FrippyErrorKind::Factoids)?
+ }
+ })
}
fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
@@ -301,3 +304,39 @@ impl<T: Database> fmt::Debug for Factoids<T> {
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/utils.rs b/src/plugins/factoids/utils.rs
index 009b46b..70ac8a7 100644
--- a/src/plugins/factoids/utils.rs
+++ b/src/plugins/factoids/utils.rs
@@ -11,7 +11,11 @@ use self::LuaError::RuntimeError;
pub fn download(_: &Lua, url: String) -> Result<String, LuaError> {
match utils::download(&url, Some(1024)) {
Ok(v) => Ok(v),
- Err(e) => Err(RuntimeError(format!("Failed to download {} - {}", url, e.to_string()))),
+ Err(e) => Err(RuntimeError(format!(
+ "Failed to download {} - {}",
+ url,
+ e.to_string()
+ ))),
}
}
diff --git a/src/plugins/help.rs b/src/plugins/help.rs
index 4dd93d7..7e3658d 100644
--- a/src/plugins/help.rs
+++ b/src/plugins/help.rs
@@ -1,8 +1,11 @@
use irc::client::prelude::*;
-use irc::error::IrcError;
use plugin::*;
+use error::FrippyError;
+use error::ErrorKind as FrippyErrorKind;
+use failure::ResultExt;
+
#[derive(PluginName, Default, Debug)]
pub struct Help;
@@ -17,18 +20,17 @@ impl Plugin for Help {
ExecutionStatus::Done
}
- fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), IrcError> {
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
panic!("Help should not use threading")
}
- fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), IrcError> {
- client.send_notice(&command.source, "Help has not been added yet.")
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
+ Ok(client
+ .send_notice(&command.source, "Help has not been added yet.")
+ .context(FrippyErrorKind::Connection)?)
}
fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
Err(String::from("Help has not been added yet."))
}
}
-
-#[cfg(test)]
-mod tests {}
diff --git a/src/plugins/keepnick.rs b/src/plugins/keepnick.rs
index bdabd90..58ac167 100644
--- a/src/plugins/keepnick.rs
+++ b/src/plugins/keepnick.rs
@@ -1,8 +1,11 @@
use irc::client::prelude::*;
-use irc::error::IrcError;
use plugin::*;
+use error::FrippyError;
+use error::ErrorKind as FrippyErrorKind;
+use failure::ResultExt;
+
#[derive(PluginName, Default, Debug)]
pub struct KeepNick;
@@ -25,9 +28,12 @@ impl KeepNick {
if client_nick != cfg_nick {
info!("Trying to switch nick from {} to {}", client_nick, cfg_nick);
- match client.send(Command::NICK(cfg_nick)) {
+ match client
+ .send(Command::NICK(cfg_nick))
+ .context(FrippyErrorKind::Connection)
+ {
Ok(_) => ExecutionStatus::Done,
- Err(e) => ExecutionStatus::Err(Box::new(e)),
+ Err(e) => ExecutionStatus::Err(e.into()),
}
} else {
ExecutionStatus::Done
@@ -45,21 +51,20 @@ impl Plugin for KeepNick {
}
}
- fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), IrcError> {
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
panic!("Tell should not use threading")
}
- fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), IrcError> {
- client.send_notice(
- &command.source,
- "This Plugin does not implement any commands.",
- )
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
+ Ok(client
+ .send_notice(
+ &command.source,
+ "This Plugin does not implement any commands.",
+ )
+ .context(FrippyErrorKind::Connection)?)
}
fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
Err(String::from("This Plugin does not implement any commands."))
}
}
-
-#[cfg(test)]
-mod tests {}
diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs
index 5b32efd..9a3ba2f 100644
--- a/src/plugins/mod.rs
+++ b/src/plugins/mod.rs
@@ -1,17 +1,8 @@
//! Collection of plugins included
-mod help;
-mod url;
-mod emoji;
-mod tell;
-mod currency;
-mod factoids;
-mod keepnick;
-
-pub use self::help::Help;
-pub use self::url::Url;
-pub use self::emoji::Emoji;
-pub use self::tell::Tell;
-pub use self::currency::Currency;
-pub use self::factoids::Factoids;
-pub use self::factoids::database;
-pub use self::keepnick::KeepNick;
+pub mod help;
+pub mod url;
+pub mod emoji;
+pub mod tell;
+pub mod currency;
+pub mod factoids;
+pub mod keepnick;
diff --git a/src/plugins/tell/mod.rs b/src/plugins/tell/mod.rs
index 3a8feeb..7052b3e 100644
--- a/src/plugins/tell/mod.rs
+++ b/src/plugins/tell/mod.rs
@@ -1,5 +1,4 @@
use irc::client::prelude::*;
-use irc::error::IrcError;
use std::time::Duration;
use std::sync::Mutex;
@@ -10,6 +9,11 @@ use humantime::format_duration;
use plugin::*;
+use error::FrippyError;
+use error::ErrorKind as FrippyErrorKind;
+use failure::Fail;
+use failure::ResultExt;
+
pub mod database;
use self::database::{Database, DbResponse};
@@ -84,7 +88,7 @@ impl<T: Database> Tell<T> {
tell.sender, human_dur, tell.message
),
) {
- return ExecutionStatus::Err(Box::new(e));
+ return ExecutionStatus::Err(e.context(FrippyErrorKind::Connection).into());
}
debug!(
"Sent {:?} from {:?} to {:?}",
@@ -123,22 +127,30 @@ impl<T: Database> Plugin for Tell<T> {
}
}
- fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), IrcError> {
+ fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
panic!("Tell should not use threading")
}
- fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), IrcError> {
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
if command.tokens.is_empty() {
- return client.send_notice(&command.source, &self.invalid_command(client));
+ return Ok(client
+ .send_notice(&command.source, &self.invalid_command(client))
+ .context(FrippyErrorKind::Connection)?);
}
- match command.tokens[0].as_ref() {
- "help" => client.send_notice(&command.source, &self.help(client)),
+ Ok(match command.tokens[0].as_ref() {
+ "help" => client
+ .send_notice(&command.source, &self.help(client))
+ .context(FrippyErrorKind::Connection),
_ => match self.tell_command(client, &command) {
- Ok(msg) => client.send_notice(&command.source, msg),
- Err(msg) => client.send_notice(&command.source, &msg),
+ Ok(msg) => client
+ .send_notice(&command.source, msg)
+ .context(FrippyErrorKind::Connection),
+ Err(msg) => client
+ .send_notice(&command.source, &msg)
+ .context(FrippyErrorKind::Connection),
},
- }
+ }?)
}
fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
diff --git a/src/plugins/url.rs b/src/plugins/url.rs
index 6f00466..fa4c6f4 100644
--- a/src/plugins/url.rs
+++ b/src/plugins/url.rs
@@ -2,7 +2,6 @@ extern crate regex;
extern crate select;
use irc::client::prelude::*;
-use irc::error::IrcError;
use self::regex::Regex;
@@ -11,9 +10,12 @@ use self::select::predicate::Name;
use plugin::*;
use utils;
+
+use self::error::*;
use error::FrippyError;
-use error::UrlError;
+use error::ErrorKind as FrippyErrorKind;
use failure::Fail;
+use failure::ResultExt;
lazy_static! {
static ref RE: Regex = Regex::new(r"(^|\s)(https?://\S+)").unwrap();
@@ -48,11 +50,11 @@ impl Url {
Some(title_text)
}
- fn url(&self, text: &str) -> Result<String, FrippyError> {
- let url = self.grep_url(text).ok_or(UrlError::MissingUrl)?;
- let body = utils::download(&url, Some(self.max_kib))?;
+ fn url(&self, text: &str) -> Result<String, UrlError> {
+ let url = self.grep_url(text).ok_or(ErrorKind::MissingUrl)?;
+ let body = utils::download(&url, Some(self.max_kib)).context(ErrorKind::Download)?;
- Ok(self.get_title(&body).ok_or(UrlError::MissingTitle)?)
+ Ok(self.get_title(&body).ok_or(ErrorKind::MissingTitle)?)
}
}
@@ -68,27 +70,48 @@ impl Plugin for Url {
}
}
- fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), IrcError> {
- match message.command {
+ fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), FrippyError> {
+ Ok(match message.command {
Command::PRIVMSG(_, ref content) => match self.url(content) {
- Ok(title) => client.send_privmsg(message.response_target().unwrap(), &title),
- Err(e) => Ok(utils::log_error(e)),
+ Ok(title) => client
+ .send_privmsg(message.response_target().unwrap(), &title)
+ .context(FrippyErrorKind::Connection)?,
+ Err(e) => Err(e).context(FrippyErrorKind::Url)?,
},
- _ => Ok(()),
- }
+ _ => (),
+ })
}
- fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), IrcError> {
- client.send_notice(
- &command.source,
- "This Plugin does not implement any commands.",
- )
+ fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
+ Ok(client
+ .send_notice(
+ &command.source,
+ "This Plugin does not implement any commands.",
+ )
+ .context(FrippyErrorKind::Connection)?)
}
fn evaluate(&self, _: &IrcClient, command: PluginCommand) -> Result<String, String> {
- self.url(&command.tokens[0]).map_err(|e| e.cause().unwrap().to_string())
+ self.url(&command.tokens[0])
+ .map_err(|e| e.cause().unwrap().to_string())
}
}
-#[cfg(test)]
-mod tests {}
+pub mod error {
+ /// A URL plugin error
+ #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
+ #[error = "UrlError"]
+ pub enum ErrorKind {
+ /// A download error
+ #[fail(display = "A download error occured")]
+ Download,
+
+ /// Missing URL error
+ #[fail(display = "No URL was found")]
+ MissingUrl,
+
+ /// Missing title error
+ #[fail(display = "No title was found")]
+ MissingTitle,
+ }
+}
diff --git a/src/utils.rs b/src/utils.rs
index cf91b37..06156be 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -4,15 +4,19 @@ use std::io::{self, Read};
use reqwest::Client;
use reqwest::header::Connection;
-use failure::Fail;
-use error::FrippyError;
+use failure::ResultExt;
+use self::error::{DownloadError, ErrorKind};
/// 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<usize>) -> Result<String, FrippyError> {
- let mut response = Client::new().get(url).header(Connection::close()).send()?;
+pub fn download(url: &str, max_kib: Option<usize>) -> Result<String, DownloadError> {
+ let mut response = Client::new()
+ .get(url)
+ .header(Connection::close())
+ .send()
+ .context(ErrorKind::Connection)?;
// 100 kibibyte buffer
let mut buf = [0; 100 * 1024];
@@ -25,7 +29,7 @@ pub fn download(url: &str, max_kib: Option<usize>) -> Result<String, FrippyError
Ok(0) => break,
Ok(len) => len,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
- Err(e) => Err(e)?,
+ Err(e) => Err(e).context(ErrorKind::Read)?,
};
bytes.extend_from_slice(&buf);
@@ -34,7 +38,7 @@ pub fn download(url: &str, max_kib: Option<usize>) -> Result<String, FrippyError
// Check if the file is too large to download
if let Some(max_kib) = max_kib {
if written > max_kib * 1024 {
- Err(FrippyError::DownloadLimit { limit: max_kib })?;
+ Err(ErrorKind::DownloadLimit)?;
}
}
}
@@ -42,12 +46,20 @@ pub fn download(url: &str, max_kib: Option<usize>) -> Result<String, FrippyError
Ok(String::from_utf8_lossy(&bytes).into_owned())
}
+pub mod error {
+ #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
+ #[error = "DownloadError"]
+ pub enum ErrorKind {
+ /// Connection Error
+ #[fail(display = "A connection error has occured")]
+ Connection,
-pub fn log_error(e: FrippyError) {
- let mut causes = e.causes();
+ /// Read Error
+ #[fail(display = "A read error has occured")]
+ Read,
- error!("{}", causes.next().unwrap());
- for cause in causes {
- error!("caused by: {}", cause);
+ /// Reached download limit error
+ #[fail(display = "Reached download limit")]
+ DownloadLimit,
}
}