diff options
Diffstat (limited to 'src/plugins/quote')
| -rw-r--r-- | src/plugins/quote/mod.rs | 238 |
1 files changed, 177 insertions, 61 deletions
diff --git a/src/plugins/quote/mod.rs b/src/plugins/quote/mod.rs index e5117eb..893b3e6 100644 --- a/src/plugins/quote/mod.rs +++ b/src/plugins/quote/mod.rs @@ -1,9 +1,10 @@ +use std::collections::HashMap; use std::fmt; use std::marker::PhantomData; use std::ops::Deref; use std::str::FromStr; -use antidote::RwLock; +use antidote::{Mutex, RwLock}; use chrono::NaiveDateTime; use irc::client::prelude::*; use rand::{thread_rng, Rng}; @@ -26,9 +27,18 @@ enum QuoteResponse { Private(String), } +#[derive(Clone)] +enum PreviousCommand { + Get, + GetUser(String, Option<i32>), + Search(String, i32), + SearchUser(String, String, i32), +} + #[derive(PluginName)] pub struct Quote<T: Database, C: Client> { quotes: RwLock<T>, + previous_map: Mutex<HashMap<String, PreviousCommand>>, phantom: PhantomData<C>, } @@ -36,6 +46,7 @@ impl<T: Database, C: Client> Quote<T, C> { pub fn new(db: T) -> Self { Quote { quotes: RwLock::new(db), + previous_map: Mutex::new(HashMap::new()), phantom: PhantomData, } } @@ -59,11 +70,13 @@ impl<T: Database, C: Client> Quote<T, C> { created: NaiveDateTime::from_timestamp(tm.sec, 0u32), }; - Ok(self + let response = self .quotes .write() .insert_quote("e) - .map(|()| "Successfully added!")?) + .map(|()| "Successfully added!")?; + + Ok(response) } fn add(&self, command: &mut PluginCommand) -> Result<&str, QuoteError> { @@ -85,87 +98,185 @@ impl<T: Database, C: Client> Quote<T, C> { fn get(&self, command: &PluginCommand) -> Result<String, QuoteError> { let quotee = &command.tokens.get(0); let channel = &command.target; - let count = if let Some(quotee) = quotee { - self.quotes.read().count_user_quotes(quotee, channel)? - } else { - self.quotes.read().count_channel_quotes(channel)? - }; + match quotee { + Some(quotee) => { + let idx = match command.tokens.get(1) { + Some(s) => Some(i32::from_str(s).context(ErrorKind::InvalidIndex)?), + None => None, + }; + + self.get_user(quotee, channel, idx) + } + None => self.get_random(channel), + } + } + + fn get_user( + &self, + quotee: &str, + channel: &str, + idx: Option<i32>, + ) -> Result<String, QuoteError> { + let count = self.quotes.read().count_user_quotes(quotee, channel)?; if count < 1 { Err(ErrorKind::NotFound)?; } - let len = command.tokens.len(); - let idx = if len < 2 || command.tokens[1].is_empty() { - thread_rng().gen_range(1, count + 1) + let mut idx = if let Some(idx) = idx { + self.previous_map.lock().insert( + channel.to_owned(), + PreviousCommand::GetUser(quotee.to_owned(), Some(idx)), + ); + + idx } else { - let idx_string = &command.tokens[1]; - let idx = i32::from_str(idx_string).context(ErrorKind::InvalidIndex)?; + self.previous_map.lock().insert( + channel.to_owned(), + PreviousCommand::GetUser(quotee.to_owned(), None), + ); - if idx < 0 { - count + idx + 1 - } else { - idx - } + thread_rng().gen_range(1, count + 1) }; - let response = if let Some(quotee) = quotee { - let quote = self - .quotes - .read() - .get_user_quote(quotee, channel, idx) - .context(ErrorKind::NotFound)?; - - format!( - "\"{}\" - {}[{}/{}]", - quote.content, quote.quotee, quote.idx, count - ) - } else { - let quote = self - .quotes - .read() - .get_channel_quote(channel, idx) - .context(ErrorKind::NotFound)?; + if idx < 0 { + idx += count + 1; + } - format!("\"{}\" - {}[{}]", quote.content, quote.quotee, quote.idx) - }; + let quote = self + .quotes + .read() + .get_user_quote(quotee, channel, idx) + .context(ErrorKind::NotFound)?; + + let response = format!( + "\"{}\" - {}[{}/{}]", + quote.content, quote.quotee, quote.idx, count + ); Ok(response) } + fn get_random(&self, channel: &str) -> Result<String, QuoteError> { + let count = self.quotes.read().count_channel_quotes(channel)?; + + if count < 1 { + Err(ErrorKind::NotFound)?; + } + self.previous_map + .lock() + .insert(channel.to_owned(), PreviousCommand::Get); + + let idx = thread_rng().gen_range(1, count + 1); + + let quote = self + .quotes + .read() + .get_channel_quote(channel, idx) + .context(ErrorKind::NotFound)?; + + Ok(format!( + "\"{}\" - {}[{}]", + quote.content, quote.quotee, quote.idx + )) + } + fn search(&self, command: &mut PluginCommand) -> Result<String, QuoteError> { if command.tokens.len() < 2 { Err(ErrorKind::InvalidCommand)?; } - let user = match command.tokens.remove(0).deref() { - "user" => Some(command.tokens.remove(0)), - "channel" => None, - _ => return Err(ErrorKind::InvalidCommand.into()), - }; let channel = &command.target; + match command.tokens.remove(0).deref() { + "user" => { + let user = command.tokens.remove(0); - if command.tokens.is_empty() { - Err(ErrorKind::InvalidCommand)?; + if command.tokens.is_empty() { + Err(ErrorKind::InvalidCommand)?; + } + + let query = command.tokens.join(" "); + self.search_user(&user, channel, &query, 0) + } + "channel" => { + if command.tokens.is_empty() { + Err(ErrorKind::InvalidCommand)?; + } + + let query = command.tokens.join(" "); + self.search_channel(channel, &query, 0) + } + _ => Err(ErrorKind::InvalidCommand.into()), } + } - let query = command.tokens.join(" "); - let quote = if let Some(user) = user { - self.quotes - .read() - .search_user_quote(&query, &user, channel, 0) - .context(ErrorKind::NotFound)? - } else { - self.quotes - .read() - .search_channel_quote(&query, channel, 0) - .context(ErrorKind::NotFound)? - }; + fn next(&self, channel: String) -> Result<String, QuoteError> { + let previous = self + .previous_map + .lock() + .get(&channel) + .cloned() + .ok_or(ErrorKind::NoPrevious)?; - Ok(format!( - "\"{}\" - {}[{}]", - quote.content, quote.quotee, quote.idx - )) + match previous { + PreviousCommand::Get => self.get_random(&channel), + PreviousCommand::GetUser(user, idx) => { + let idx = idx.map(|idx| if idx < 0 { idx - 1 } else { idx + 1 }); + + self.get_user(&user, &channel, idx) + } + PreviousCommand::Search(query, offset) => { + self.search_channel(&channel, &query, offset + 1) + } + PreviousCommand::SearchUser(user, query, offset) => { + self.search_user(&user, &channel, &query, offset + 1) + } + } + } + + fn search_user( + &self, + user: &str, + channel: &str, + query: &str, + offset: i32, + ) -> Result<String, QuoteError> { + self.previous_map.lock().insert( + channel.to_owned(), + PreviousCommand::SearchUser(user.to_owned(), query.to_owned(), offset), + ); + + let quote = self + .quotes + .read() + .search_user_quote(&query, &user, channel, offset) + .context(ErrorKind::NotFound)?; + + let response = format!("\"{}\" - {}[{}]", quote.content, quote.quotee, quote.idx); + + Ok(response) + } + + fn search_channel( + &self, + channel: &str, + query: &str, + offset: i32, + ) -> Result<String, QuoteError> { + self.previous_map.lock().insert( + channel.to_owned(), + PreviousCommand::Search(query.to_owned(), offset), + ); + + let quote = self + .quotes + .read() + .search_channel_quote(&query, channel, offset) + .context(ErrorKind::NotFound)?; + + let response = format!("\"{}\" - {}[{}]", quote.content, quote.quotee, quote.idx); + + Ok(response) } fn info(&self, command: &PluginCommand) -> Result<String, QuoteError> { @@ -223,7 +334,7 @@ impl<T: Database, C: Client> Quote<T, C> { fn help(&self) -> &str { "usage: quotes <subcommand>\r\n\ - subcommands: add, get, search info, help" + subcommands: add, get, search, next, info, help" } } @@ -260,6 +371,7 @@ impl<T: Database, C: FrippyClient> Plugin for Quote<T, C> { "add" => self.add(&mut command).map(|s| Private(s.to_owned())), "get" => self.get(&command).map(Public), "search" => self.search(&mut command).map(Public), + "next" => self.next(command.target).map(Public), "info" => self.info(&command).map(Public), "help" => Ok(Private(self.help().to_owned())), _ => Err(ErrorKind::InvalidCommand.into()), @@ -314,6 +426,10 @@ pub mod error { #[fail(display = "Invalid index")] InvalidIndex, + /// No previous command error + #[fail(display = "No previous command was found for this channel")] + NoPrevious, + /// Private message error #[fail(display = "You can only add quotes in channel messages")] PrivateMessageNotAllowed, |
