From 5928afc3bf83661cd3b11130a31a2d97ef135a9e Mon Sep 17 00:00:00 2001 From: Jokler Date: Sat, 4 Nov 2017 22:46:39 +0100 Subject: Add MySql as a possible database for the Factoids plugin --- migrations/2017-11-03-164322_create_factoids/up.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 migrations/2017-11-03-164322_create_factoids/up.sql (limited to 'migrations/2017-11-03-164322_create_factoids/up.sql') diff --git a/migrations/2017-11-03-164322_create_factoids/up.sql b/migrations/2017-11-03-164322_create_factoids/up.sql new file mode 100644 index 0000000..84e9f9d --- /dev/null +++ b/migrations/2017-11-03-164322_create_factoids/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE factoids ( + name VARCHAR(32) PRIMARY KEY, + content VARCHAR(5000) NOT NULL +) -- cgit v1.2.3-70-g09d2 From 4b5693d3c6781a5ca4ef32f43f3994a65020c933 Mon Sep 17 00:00:00 2001 From: Jokler Date: Sat, 16 Dec 2017 18:13:02 +0100 Subject: Add more info to factoids and save old versions --- Cargo.lock | 78 +++++++++-- Cargo.toml | 19 ++- bin/main.rs | 30 ++++- migrations/.gitkeep | 0 .../2017-11-03-164322_create_factoids/up.sql | 8 +- src/lib.rs | 9 +- src/plugins/factoids/database.rs | 120 +++++++++++------ src/plugins/factoids/mod.rs | 146 +++++++++++++++++---- 8 files changed, 322 insertions(+), 88 deletions(-) delete mode 100644 migrations/.gitkeep (limited to 'migrations/2017-11-03-164322_create_factoids/up.sql') diff --git a/Cargo.lock b/Cargo.lock index 91693d5..936d8a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -220,32 +220,40 @@ dependencies = [ [[package]] name = "diesel" -version = "0.16.0" +version = "1.0.0-rc1" 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.0 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel_derives 1.0.0-rc1 (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.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "diesel_codegen" -version = "0.16.0" +name = "diesel_derives" +version = "1.0.0-rc1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "diesel 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "diesel_infer_schema 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dotenv 0.10.1 (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 = "diesel_infer_schema" -version = "0.16.0" +version = "1.0.0-rc1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "diesel 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "infer_schema_macros 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "diesel_migrations" +version = "1.0.0-rc1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "migrations_internals 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)", + "migrations_macros 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -350,9 +358,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "frippy" version = "0.3.1" dependencies = [ + "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.177 (registry+https://github.com/rust-lang/crates.io-index)", - "diesel 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", - "diesel_codegen 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel_infer_schema 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)", + "diesel_migrations 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)", "dotenv 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "frippy_derive 0.1.0", "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", @@ -502,6 +512,25 @@ name = "if_chain" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "infer_schema_internals" +version = "1.0.0-rc1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "diesel 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "infer_schema_macros" +version = "1.0.0-rc1" +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.0.0-rc1 (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 = "iovec" version = "0.1.1" @@ -628,6 +657,24 @@ dependencies = [ "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "migrations_internals" +version = "1.0.0-rc1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "diesel 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "migrations_macros" +version = "1.0.0-rc1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "migrations_internals 1.0.0-rc1 (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" @@ -1389,9 +1436,10 @@ dependencies = [ "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" "checksum debug_unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9a032eac705ca39214d169f83e3d3da290af06d8d1d344d1baad2fd002dca4b3" "checksum derive-error-chain 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9ca9ade651388daad7c993f005d0d20c4f6fe78c1cdc93e95f161c6f5ede4a" -"checksum diesel 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "304226fa7a3982b0405f6bb95dd9c10c3e2000709f194038a60ec2c277150951" -"checksum diesel_codegen 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18a42ca5c9b660add51d58bc5a50a87123380e1e458069c5504528a851ed7384" -"checksum diesel_infer_schema 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bf1957ff5cd3b04772e43c162c2f69c2aa918080ff9b020276792d236be8be52" +"checksum diesel 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6b9e512c7fbcc7240848252ff620ed5f0e996e25d5c694b2b535d27db7465c6" +"checksum diesel_derives 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)" = "a631faea061a7b5ab85e842866da57dbc31cc5fe715397a5e5a5b0effd7146b9" +"checksum diesel_infer_schema 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cee2c5104f0258a4461c4c8694e870161cb974f4de8609fae3d496e26336590" +"checksum diesel_migrations 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)" = "e1fd168d237ae0bd9e8bfdcb82ba87a4b72e4c1d9edd864242a67149c7f3c960" "checksum dotenv 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d6f0e2bb24d163428d8031d3ebd2d2bd903ad933205a97d0f18c7c1aade380f3" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3" @@ -1419,6 +1467,8 @@ dependencies = [ "checksum hyper-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c81fa95203e2a6087242c38691a0210f23e9f3f8f944350bd676522132e2985" "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.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2af8e23352c51aff14633500bfff361e9ea310375044b40dd8939e2defccd5c" +"checksum infer_schema_macros 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa05e92983b3f9af1e7508f7d4981452542d505640d1b328fe74f4466c82b953" "checksum iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6e8b9c2247fcf6c6a1151f1156932be5606c9fd6f55a2d7f9fc1cb29386b2f7" "checksum irc 0.12.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c5053c25509042e963baa67d0d648add9f72b7487784505a5d5a3054d136a615" "checksum itertools 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3f2be4da1690a039e9ae5fd575f706a63ad5a2120f161b1d653c9da3930dd21" @@ -1435,6 +1485,8 @@ dependencies = [ "checksum markup5ever 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff834ac7123c6a37826747e5ca09db41fd7a83126792021c2e636ad174bb77d3" "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 migrations_internals 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)" = "c39531d07a48b5920d19310f9ba644667aac56edd933a6e7649893551756ad50" +"checksum migrations_macros 1.0.0-rc1 (registry+https://github.com/rust-lang/crates.io-index)" = "12d6c72b7dae5fb40009c6313ef5a2075aa25f479a8a51f6a487abef887a7a2c" "checksum mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e00e17be181010a91dbfefb01660b17311059dc8c7f48b9017677721e732bd" "checksum mime_guess 2.0.0-alpha.3 (registry+https://github.com/rust-lang/crates.io-index)" = "013572795763289e14710c7b279461295f2673b2b338200c235082cd7ca9e495" "checksum mio 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0e8411968194c7b139e9105bc4ae7db0bae232af087147e72f0616ebf5fdb9cb" diff --git a/Cargo.toml b/Cargo.toml index 5ff779f..5f535e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ path = "bin/main.rs" doc = false [features] -mysql = ["diesel", "diesel_codegen", "dotenv"] +mysql = ["diesel", "diesel_infer_schema", "diesel_migrations", "dotenv"] [dependencies] irc = "0.12.5" @@ -27,13 +27,14 @@ tokio-core = "0.1.10" futures = "0.1.16" log = "0.3.8" time = "0.1" -rlua = "0.9.2" +rlua = "0.9.3" reqwest = "0.8.0" select = "0.4.2" regex = "0.2.2" lazy_static = "0.2.9" serde = "1.0.15" serde_json = "1.0.3" +chrono = "0.4.0" glob = "0.2" frippy_derive = { path = "frippy_derive" } @@ -42,13 +43,19 @@ frippy_derive = { path = "frippy_derive" } git = 'https://github.com/Jokler/unicode_names' branch = 'update-to-latest-unicode' + [dependencies.diesel] -version = "0.16.0" +version = "1.0.0-beta1" +optional = true +features = ["mysql", "chrono"] + +[dependencies.diesel_infer_schema] +version = "1.0.0-beta1" optional = true features = ["mysql"] -[dependencies.diesel_codegen] -version = "0.16.0" +[dependencies.diesel_migrations] +version = "1.0.0-beta1" optional = true features = ["mysql"] @@ -58,4 +65,4 @@ optional = true [dependencies.clippy] version = "*" -optional = true \ No newline at end of file +optional = true diff --git a/bin/main.rs b/bin/main.rs index 1597a70..7869548 100644 --- a/bin/main.rs +++ b/bin/main.rs @@ -4,9 +4,16 @@ extern crate tokio_core; extern crate glob; extern crate futures; +#[cfg(feature = "mysql")] +#[macro_use] +extern crate diesel_migrations; +#[cfg(feature = "mysql")] +extern crate diesel; + #[macro_use] extern crate log; +#[cfg(not(feature = "mysql"))] use std::collections::HashMap; use log::{LogRecord, LogLevel, LogLevelFilter, LogMetadata}; @@ -18,6 +25,9 @@ use glob::glob; use frippy::plugins; use frippy::Config; +#[cfg(feature = "mysql")] +embed_migrations!(); + struct Logger; impl log::Log for Logger { @@ -44,7 +54,6 @@ impl log::Log for Logger { } fn main() { - let log_level = if cfg!(debug_assertions) { LogLevelFilter::Debug } else { @@ -87,7 +96,10 @@ fn main() { let mut disabled_plugins = None; if let &Some(ref options) = &config.options { if let Some(disabled) = options.get("disabled_plugins") { - disabled_plugins = Some(disabled.split(",").map(|p| p.trim()).collect::>()); + disabled_plugins = Some(disabled + .split(",") + .map(|p| p.trim()) + .collect::>()); } } @@ -97,8 +109,22 @@ fn main() { bot.add_plugin(plugins::Emoji::new()); bot.add_plugin(plugins::Currency::new()); bot.add_plugin(plugins::KeepNick::new()); + #[cfg(feature = "mysql")] + { + use diesel; + use diesel::Connection; + match diesel::mysql::MysqlConnection::establish("mysql://user:password@address/db") { + Ok(conn) => { + embedded_migrations::run(&conn).unwrap(); + bot.add_plugin(plugins::Factoids::new(conn)); + } + Err(e) => error!("Failed to connect to database: {}", e), + } + } + #[cfg(not(feature = "mysql"))] bot.add_plugin(plugins::Factoids::new(HashMap::new())); + if let Some(disabled_plugins) = disabled_plugins { for name in disabled_plugins { if let None = bot.remove_plugin(name) { diff --git a/migrations/.gitkeep b/migrations/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/migrations/2017-11-03-164322_create_factoids/up.sql b/migrations/2017-11-03-164322_create_factoids/up.sql index 84e9f9d..784a3f4 100644 --- a/migrations/2017-11-03-164322_create_factoids/up.sql +++ b/migrations/2017-11-03-164322_create_factoids/up.sql @@ -1,4 +1,8 @@ CREATE TABLE factoids ( - name VARCHAR(32) PRIMARY KEY, - content VARCHAR(5000) NOT NULL + name VARCHAR(32) NOT NULL, + idx INTEGER NOT NULL, + content TEXT NOT NULL, + author VARCHAR(32) NOT NULL, + created TIMESTAMP NOT NULL, + PRIMARY KEY (name, idx) ) diff --git a/src/lib.rs b/src/lib.rs index 1f5514e..4407e6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,8 +35,10 @@ #[macro_use] extern crate diesel; #[cfg(feature = "mysql")] +extern crate diesel_infer_schema; +#[cfg(feature = "mysql")] #[macro_use] -extern crate diesel_codegen; +extern crate diesel_migrations; #[macro_use] extern crate log; @@ -48,12 +50,15 @@ extern crate frippy_derive; extern crate irc; extern crate futures; extern crate tokio_core; +extern crate regex; +extern crate chrono; +extern crate time; pub mod plugin; pub mod plugins; -use std::fmt; use std::collections::HashMap; +use std::fmt; use std::thread::spawn; use std::sync::Arc; 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; + fn insert(&mut self, factoid: &NewFactoid) -> DbResponse; + fn get(&self, name: &str, idx: i32) -> Option; + fn count(&self, name: &str) -> Result; } -impl Database for HashMap { - 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 { - self.get(name).cloned() + fn get(&self, name: &str, idx: i32) -> Option { + 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 { + 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 { + fn get(&self, name: &str, idx: i32) -> Option { factoids::table - .filter(factoids::columns::name.eq(name)) + .find((name, idx)) .limit(1) .load::(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 { + let count: Result = 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 Factoids { } 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 Plugin for Factoids { tokens: t, }; - self.exec(server, c) + self.exec(server, c, false) } else { Ok(()) @@ -165,7 +258,8 @@ impl Plugin for Factoids { 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), } } -- cgit v1.2.3-70-g09d2