diff --git a/examples/simple_cmd.rs b/examples/simple_cmd.rs new file mode 100644 index 0000000..3841648 --- /dev/null +++ b/examples/simple_cmd.rs @@ -0,0 +1,43 @@ +extern crate irc; + +use std::default::Default; +use irc::client::prelude::*; +use irc::client::data::message::ToMessage; + +// This is the same as simple.rs, except we use an Iterator over Commands +// instead of an Iterator over Messages. A Command is basically a parsed Message, +// so Commands and Messages are interchangeable. It is up to the library user to +// choose one. + +fn main() { + let config = Config { + nickname: Some(format!("pickles")), + alt_nicks: Some(vec![format!("bananas"), format!("apples")]), + server: Some(format!("irc.fyrechat.net")), + channels: Some(vec![format!("#vana")]), + .. Default::default() + }; + let irc_server = IrcServer::from_config(config).unwrap(); + // The wrapper provides us with methods like send_privmsg(...) and identify(...) + let server = Wrapper::new(&irc_server); + server.identify().unwrap(); + for command in server.iter_cmd() { + // Ignore errors + // Use of unwrap() with iter_cmd() is discouraged because iter_cmd() is still unstable + // and has trouble converting some custom Messages into Commands + match command { + Ok(cmd) => { + print!("{}", cmd.to_message().into_string()); + match cmd { + Command::PRIVMSG(target, text) => { + if text[..].contains("pickles") { + server.send_privmsg(&target[..], "Hi!").unwrap(); + } + }, + _ => () + } + }, + Err(_) => () + }; + } +} diff --git a/src/client/data/command.rs b/src/client/data/command.rs index b4fbeb5..33a8e44 100644 --- a/src/client/data/command.rs +++ b/src/client/data/command.rs @@ -1149,6 +1149,12 @@ impl Command { return Err(invalid_input()) }) } + + /// Converts an `IoResult` holding a Message into an `IoResult` + #[unstable = "This feature is still relatively new."] + pub fn from_message_io(m: IoResult) -> IoResult { + m.and_then(|msg| Command::from_message(&msg)) + } } /// A list of all of the subcommands for the capabilities extension. diff --git a/src/client/server/mod.rs b/src/client/server/mod.rs index 6fad534..a60748f 100644 --- a/src/client/server/mod.rs +++ b/src/client/server/mod.rs @@ -4,6 +4,7 @@ use std::borrow::ToOwned; use std::collections::HashMap; use std::old_io::{BufferedReader, BufferedWriter, IoError, IoErrorKind, IoResult}; use std::sync::{Mutex, RwLock}; +use std::iter::Map; use client::conn::{Connection, NetStream}; use client::data::{Command, Config, Message, Response, User}; use client::data::Command::{JOIN, NICK, NICKSERV, PONG, MODE}; @@ -24,6 +25,9 @@ pub trait Server<'a, T, U> { /// Gets an Iterator over Messages received by this Server. #[stable] fn iter(&'a self) -> ServerIterator<'a, T, U>; + /// Gets an Iterator over Commands received by this Server. + #[unstable = "Feature is still relatively new."] + fn iter_cmd(&'a self) -> ServerCmdIterator<'a, T, U>; /// Gets a list of Users in the specified channel. This will be none if the channel is not /// being tracked, or if tracking is not supported altogether. #[stable] @@ -95,6 +99,10 @@ impl<'a, T: IrcReader, U: IrcWriter> Server<'a, T, U> for IrcServer { ServerIterator::new(self) } + fn iter_cmd(&'a self) -> ServerCmdIterator<'a, T, U> { + self.iter().map(Command::from_message_io) + } + #[cfg(not(feature = "nochanlists"))] fn list_users(&self, chan: &str) -> Option> { self.chanlists.lock().unwrap().get(&chan.to_owned()).cloned() @@ -257,6 +265,12 @@ pub struct ServerIterator<'a, T: IrcReader, U: IrcWriter> { server: &'a IrcServer } +/// An Iterator over an IrcServer's incoming Commands. +/// Commands and Messages are interchangeable. This is just a convenient way to get +/// a sanitized, already-parsed IRC message. +pub type ServerCmdIterator<'a, T, U> = + Map, fn(IoResult) -> IoResult>; + #[unstable = "Design is liable to change to accomodate new functionality."] impl<'a, T: IrcReader, U: IrcWriter> ServerIterator<'a, T, U> { /// Creates a new ServerIterator for the desired IrcServer. @@ -312,6 +326,7 @@ mod test { use client::data::{Config, User}; use client::data::command::Command::PRIVMSG; use client::data::kinds::IrcReader; + use client::data::message::ToMessage; pub fn test_config() -> Config { Config { @@ -344,6 +359,20 @@ mod test { assert_eq!(&messages[..], exp); } + #[test] + fn iterator_cmd() { + let exp = "PRIVMSG test :Hi!\r\nPRIVMSG test :This is a test!\r\n\ + JOIN #test\r\n"; + let server = IrcServer::from_connection(test_config(), Connection::new( + MemReader::new(exp.as_bytes().to_vec()), NullWriter + )); + let mut messages = String::new(); + for command in server.iter_cmd() { + messages.push_str(&command.unwrap().to_message().into_string()); + } + assert_eq!(&messages[..], exp); + } + #[test] fn handle_message() { let value = "PING :irc.test.net\r\n:irc.test.net 376 test :End of /MOTD command.\r\n"; diff --git a/src/client/server/utils.rs b/src/client/server/utils.rs index 169f398..e6acb2a 100644 --- a/src/client/server/utils.rs +++ b/src/client/server/utils.rs @@ -9,7 +9,7 @@ use client::data::Command::{OPER, PASS, PONG, PRIVMSG, QUIT, SAMODE, SANICK, TOP use client::data::command::CapSubCommand::{END, REQ}; use client::data::kinds::{IrcReader, IrcWriter}; #[cfg(feature = "ctcp")] use time::get_time; -use client::server::{Server, ServerIterator}; +use client::server::{Server, ServerIterator, ServerCmdIterator}; /// Functionality-providing wrapper for Server. /// Wrappers are currently not thread-safe, and should be created per-thread, as needed. @@ -31,6 +31,10 @@ impl<'a, T: IrcReader, U: IrcWriter> Server<'a, T, U> for Wrapper<'a, T, U> { self.server.iter() } + fn iter_cmd(&'a self) -> ServerCmdIterator<'a, T, U> { + self.server.iter_cmd() + } + fn list_users(&self, chan: &str) -> Option> { self.server.list_users(chan) }