diff options
Diffstat (limited to 'src/plugins')
| -rw-r--r-- | src/plugins/factoids/database.rs | 120 | ||||
| -rw-r--r-- | src/plugins/factoids/mod.rs | 146 |
2 files changed, 203 insertions, 63 deletions
diff --git a/src/plugins/factoids/database.rs b/src/plugins/factoids/database.rs index dbf136c..522c9d0 100644 --- a/src/plugins/factoids/database.rs +++ b/src/plugins/factoids/database.rs @@ -9,66 +9,112 @@ use diesel::prelude::*; #[cfg(feature = "mysql")] use diesel::mysql::MysqlConnection; +use chrono::NaiveDateTime; + +pub enum DbResponse { + Success, + Failed(&'static str), +} + +#[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 { - fn insert(&mut self, name: &str, content: &str) -> Option<()>; - fn get(&self, name: &str) -> Option<String>; + fn insert(&mut self, factoid: &NewFactoid) -> DbResponse; + fn get(&self, name: &str, idx: i32) -> Option<Factoid>; + fn count(&self, name: &str) -> Result<i32, &'static str>; } -impl Database for HashMap<String, String> { - fn insert(&mut self, name: &str, content: &str) -> Option<()> { - self.insert(String::from(name), String::from(content)).map(|_| ()) +// HashMap +impl Database for HashMap<(String, i32), Factoid> { + fn insert(&mut self, factoid: &NewFactoid) -> DbResponse { + let factoid = Factoid { + name: String::from(factoid.name), + idx: factoid.idx, + content: factoid.content.to_string(), + author: factoid.author.to_string(), + created: factoid.created, + }; + + let name = String::from(factoid.name.clone()); + match self.insert((name, factoid.idx), factoid) { + None => DbResponse::Success, + Some(_) => DbResponse::Failed("Factoid was overwritten"), + } } - fn get(&self, name: &str) -> Option<String> { - self.get(name).cloned() + fn get(&self, name: &str, idx: i32) -> Option<Factoid> { + self.get(&(String::from(name), idx)).map(|f| f.clone()) } -} -#[cfg(feature = "mysql")] -#[derive(Queryable)] -struct Factoid { - pub name: String, - pub content: String, + fn count(&self, name: &str) -> Result<i32, &'static str> { + Ok(self.iter() + .filter(|&(&(ref n, _), _)| n == name) + .count() as i32) + } } +// MySql #[cfg(feature = "mysql")] table! { - factoids (name) { + factoids (name, idx) { name -> Varchar, - content -> Varchar, + idx -> Integer, + content -> Text, + author -> Varchar, + created -> Timestamp, } } #[cfg(feature = "mysql")] -#[derive(Insertable)] -#[table_name="factoids"] -struct NewFactoid<'a> { - pub name: &'a str, - pub content: &'a str, -} - - -#[cfg(feature = "mysql")] impl Database for MysqlConnection { - fn insert(&mut self, name: &str, content: &str) -> Option<()> { - let factoid = NewFactoid { - name: name, - content: content, - }; + fn insert(&mut self, factoid: &NewFactoid) -> DbResponse { + use diesel; - ::diesel::insert(&factoid) - .into(factoids::table) - .execute(self) - .ok() - .map(|_| ()) + match diesel::insert_into(factoids::table) + .values(factoid) + .execute(self) { + Ok(_) => DbResponse::Success, + Err(_) => DbResponse::Failed("Database error - possible duplicate"), + } } - fn get(&self, name: &str) -> Option<String> { + fn get(&self, name: &str, idx: i32) -> Option<Factoid> { factoids::table - .filter(factoids::columns::name.eq(name)) + .find((name, idx)) .limit(1) .load::<Factoid>(self) .ok() - .and_then(|v| v.first().map(|f| f.content.clone())) + .and_then(|v| v.into_iter().next()) + } + + fn count(&self, name: &str) -> Result<i32, &'static str> { + let count: Result<i64, _> = factoids::table + .filter(factoids::columns::name.eq(name)) + .count() + .first(self); + + match count { + Ok(c) => Ok(c as i32), + Err(_) => Err("Database Error"), + } } } diff --git a/src/plugins/factoids/mod.rs b/src/plugins/factoids/mod.rs index 5f9f99a..bb83700 100644 --- a/src/plugins/factoids/mod.rs +++ b/src/plugins/factoids/mod.rs @@ -1,15 +1,18 @@ extern crate rlua; use std::fmt; +use std::str::FromStr; +use std::sync::Mutex; use self::rlua::prelude::*; use irc::client::prelude::*; use irc::error::Error as IrcError; -use std::sync::Mutex; +use time; +use chrono::NaiveDateTime; use plugin::*; mod database; -use self::database::Database; +use self::database::{Database, DbResponse}; static LUA_SANDBOX: &'static str = include_str!("sandbox.lua"); @@ -33,50 +36,140 @@ impl<T: Database> Factoids<T> { } fn add(&self, server: &IrcServer, command: &mut PluginCommand) -> Result<(), IrcError> { - if command.tokens.len() < 2 { return self.invalid_command(server, command); } let name = command.tokens.remove(0); - - try_lock!(self.factoids) - .insert(&name, &command.tokens.join(" ")); - - server.send_notice(&command.source, "Successfully added") + let content = command.tokens.join(" "); + let count = match try_lock!(self.factoids).count(&name) { + Ok(c) => c, + Err(e) => return server.send_notice(&command.source, e), + }; + + let tm = time::now().to_timespec(); + + let factoid = database::NewFactoid { + name: &name, + idx: count, + content: &content, + author: &command.source, + created: NaiveDateTime::from_timestamp(tm.sec, tm.nsec as u32), + }; + + match try_lock!(self.factoids).insert(&factoid) { + DbResponse::Success => server.send_notice(&command.source, "Successfully added"), + DbResponse::Failed(e) => server.send_notice(&command.source, &e), + } } fn get(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> { - if command.tokens.len() < 1 { - self.invalid_command(server, command) + let (name, idx) = match command.tokens.len() { + 0 => return self.invalid_command(server, command), + 1 => { + let name = &command.tokens[0]; + let count = match try_lock!(self.factoids).count(name) { + Ok(c) => c, + Err(e) => return server.send_notice(&command.source, e), + }; + + if count < 1 { + return server.send_notice(&command.source, &format!("{} does not exist", name)); + } - } else { - let name = &command.tokens[0]; - let factoids = try_lock!(self.factoids); - let factoid = match factoids.get(name) { - Some(v) => v, - None => return self.invalid_command(server, command), - }; + (name, count - 1) + } + _ => { + let name = &command.tokens[0]; + let idx = match i32::from_str(&command.tokens[1]) { + Ok(i) => i, + Err(_) => return server.send_notice(&command.source, "Invalid index"), + }; + + (name, idx) + } + }; + + let factoid = match try_lock!(self.factoids).get(name, idx) { + Some(v) => v, + None => return server.send_notice(&command.source, &format!("{}~{} does not exist", name, idx)), + }; + + server.send_privmsg(&command.target, + &format!("{}: {}", factoid.name, factoid.content)) + } + + fn info(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> { + + match command.tokens.len() { + 0 => self.invalid_command(server, command), + 1 => { + let name = &command.tokens[0]; + let count = match try_lock!(self.factoids).count(name) { + Ok(c) => c, + Err(e) => return server.send_notice(&command.source, e), + }; + + match count { + 0 => server.send_notice(&command.source, &format!("{} does not exist", name)), + 1 => { + server.send_privmsg(&command.target, + &format!("There is 1 version of {}", name)) + } + _ => { + server.send_privmsg(&command.target, + &format!("There are {} versions of {}", count, name)) + } + } + } + _ => { + let name = &command.tokens[0]; + let idx = match i32::from_str(&command.tokens[1]) { + Ok(i) => i, + Err(_) => return server.send_notice(&command.source, "Invalid index"), + }; + + let factoid = match try_lock!(self.factoids).get(name, idx) { + Some(v) => v, + None => { + return server.send_notice(&command.source, + &format!("{}~{} does not exist", name, idx)) + } + }; + + server.send_privmsg(&command.target, + &format!("{}: Added by {} at {} UTC", + name, + factoid.author, + factoid.created)) + } - server.send_privmsg(&command.target, &format!("{}: {}", name, factoid)) } } - fn exec(&self, server: &IrcServer, mut command: PluginCommand) -> Result<(), IrcError> { + fn exec(&self, + server: &IrcServer, + mut command: PluginCommand, + error: bool) + -> Result<(), IrcError> { if command.tokens.len() < 1 { self.invalid_command(server, &command) } else { let name = command.tokens.remove(0); + let count = match try_lock!(self.factoids).count(&name) { + Ok(c) => c, + Err(e) => return server.send_notice(&command.source, e), + }; - let factoids = try_lock!(self.factoids); - let factoid = match factoids.get(&name) { - Some(v) => v, - None => return self.invalid_command(server, &command), + let factoid = match try_lock!(self.factoids).get(&name, count - 1) { + Some(v) => v.content, + None if error => return self.invalid_command(server, &command), + None => return Ok(()), }; - let value = if factoid.starts_with(">") { + let value = &if factoid.starts_with(">") { let factoid = String::from(&factoid[1..]); if factoid.starts_with(">") { @@ -149,7 +242,7 @@ impl<T: Database> Plugin for Factoids<T> { tokens: t, }; - self.exec(server, c) + self.exec(server, c, false) } else { Ok(()) @@ -165,7 +258,8 @@ impl<T: Database> Plugin for Factoids<T> { match sub_command.as_ref() { "add" => self.add(server, &mut command), "get" => self.get(server, &command), - "exec" => self.exec(server, command), + "info" => self.info(server, &command), + "exec" => self.exec(server, command, true), _ => self.invalid_command(server, &command), } } |
