Added Command iterator. Added a test and a use case example.

This commit is contained in:
Filipe Gonçalves 2015-02-23 22:04:05 +00:00
parent 0ac5737f7f
commit 04dccf5ce2
4 changed files with 83 additions and 1 deletions

43
examples/simple_cmd.rs Normal file
View file

@ -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(_) => ()
};
}
}

View file

@ -1145,6 +1145,12 @@ impl Command {
return Err(invalid_input()) return Err(invalid_input())
}) })
} }
/// Converts an `IoResult<Message>` holding a Message into an `IoResult<Command>`
#[unstable = "This feature is still relatively new."]
pub fn from_message_io(m: IoResult<Message>) -> IoResult<Command> {
m.and_then(|msg| Command::from_message(&msg))
}
} }
/// A list of all of the subcommands for the capabilities extension. /// A list of all of the subcommands for the capabilities extension.

View file

@ -4,6 +4,7 @@ use std::borrow::ToOwned;
use std::collections::HashMap; use std::collections::HashMap;
use std::old_io::{BufferedReader, BufferedWriter, IoError, IoErrorKind, IoResult}; use std::old_io::{BufferedReader, BufferedWriter, IoError, IoErrorKind, IoResult};
use std::sync::{Mutex, RwLock}; use std::sync::{Mutex, RwLock};
use std::iter::Map;
use client::conn::{Connection, NetStream}; use client::conn::{Connection, NetStream};
use client::data::{Command, Config, Message, Response, User}; use client::data::{Command, Config, Message, Response, User};
use client::data::Command::{JOIN, NICK, NICKSERV, PONG, MODE}; 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. /// Gets an Iterator over Messages received by this Server.
#[stable] #[stable]
fn iter(&'a self) -> ServerIterator<'a, T, U>; 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 /// 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. /// being tracked, or if tracking is not supported altogether.
#[stable] #[stable]
@ -95,6 +99,10 @@ impl<'a, T: IrcReader, U: IrcWriter> Server<'a, T, U> for IrcServer<T, U> {
ServerIterator::new(self) ServerIterator::new(self)
} }
fn iter_cmd(&'a self) -> ServerCmdIterator<'a, T, U> {
self.iter().map(Command::from_message_io)
}
#[cfg(not(feature = "nochanlists"))] #[cfg(not(feature = "nochanlists"))]
fn list_users(&self, chan: &str) -> Option<Vec<User>> { fn list_users(&self, chan: &str) -> Option<Vec<User>> {
self.chanlists.lock().unwrap().get(&chan.to_owned()).cloned() self.chanlists.lock().unwrap().get(&chan.to_owned()).cloned()
@ -257,6 +265,12 @@ pub struct ServerIterator<'a, T: IrcReader, U: IrcWriter> {
server: &'a IrcServer<T, U> server: &'a IrcServer<T, U>
} }
/// 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<ServerIterator<'a, T, U>, fn(IoResult<Message>) -> IoResult<Command>>;
#[unstable = "Design is liable to change to accomodate new functionality."] #[unstable = "Design is liable to change to accomodate new functionality."]
impl<'a, T: IrcReader, U: IrcWriter> ServerIterator<'a, T, U> { impl<'a, T: IrcReader, U: IrcWriter> ServerIterator<'a, T, U> {
/// Creates a new ServerIterator for the desired IrcServer. /// Creates a new ServerIterator for the desired IrcServer.
@ -312,6 +326,7 @@ mod test {
use client::data::{Config, User}; use client::data::{Config, User};
use client::data::command::Command::PRIVMSG; use client::data::command::Command::PRIVMSG;
use client::data::kinds::IrcReader; use client::data::kinds::IrcReader;
use client::data::message::ToMessage;
pub fn test_config() -> Config { pub fn test_config() -> Config {
Config { Config {
@ -344,6 +359,20 @@ mod test {
assert_eq!(&messages[..], exp); 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] #[test]
fn handle_message() { fn handle_message() {
let value = "PING :irc.test.net\r\n:irc.test.net 376 test :End of /MOTD command.\r\n"; let value = "PING :irc.test.net\r\n:irc.test.net 376 test :End of /MOTD command.\r\n";

View file

@ -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::command::CapSubCommand::{END, REQ};
use client::data::kinds::{IrcReader, IrcWriter}; use client::data::kinds::{IrcReader, IrcWriter};
#[cfg(feature = "ctcp")] use time::get_time; #[cfg(feature = "ctcp")] use time::get_time;
use client::server::{Server, ServerIterator}; use client::server::{Server, ServerIterator, ServerCmdIterator};
/// Functionality-providing wrapper for Server. /// Functionality-providing wrapper for Server.
/// Wrappers are currently not thread-safe, and should be created per-thread, as needed. /// 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() self.server.iter()
} }
fn iter_cmd(&'a self) -> ServerCmdIterator<'a, T, U> {
self.server.iter_cmd()
}
fn list_users(&self, chan: &str) -> Option<Vec<User>> { fn list_users(&self, chan: &str) -> Option<Vec<User>> {
self.server.list_users(chan) self.server.list_users(chan)
} }