From ed84a41e5709f8035ac893a9572638e1ace085ae Mon Sep 17 00:00:00 2001 From: Simon Bernier St-Pierre <sbernierstpierre@gmail.com> Date: Wed, 31 Dec 2014 18:39:00 -0500 Subject: [PATCH] Implement ToMessage for Command and Message and use ToMessage in Connection. --- src/conn.rs | 20 +++++++++++--------- src/data/command.rs | 44 ++++++++++++++++++++++++-------------------- src/data/message.rs | 16 ++++++++++++++++ src/server/mod.rs | 23 ++++++++++++----------- 4 files changed, 63 insertions(+), 40 deletions(-) diff --git a/src/conn.rs b/src/conn.rs index 67b744c..fcb2e73 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -6,7 +6,7 @@ use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; #[cfg(feature = "encode")] use encoding::{DecoderTrap, EncoderTrap, Encoding}; #[cfg(feature = "encode")] use encoding::label::encoding_from_whatwg_label; use data::kinds::{IrcReader, IrcWriter}; -use data::message::Message; +use data::message::ToMessage; #[cfg(feature = "ssl")] use openssl::ssl::{SslContext, SslMethod, SslStream}; #[cfg(feature = "ssl")] use openssl::ssl::error::SslError; @@ -26,7 +26,7 @@ type NetReaderWriterPair = (BufferedReader<NetStream>, BufferedWriter<NetStream> impl Connection<BufferedReader<NetStream>, BufferedWriter<NetStream>> { /// Creates a thread-safe TCP connection to the specified server. #[experimental] - pub fn connect(host: &str, port: u16) -> IoResult<NetConnection> { + pub fn connect(host: &str, port: u16) -> IoResult<NetConnection> { let (reader, writer) = try!(Connection::connect_internal(host, port)); Ok(Connection::new(reader, writer)) } @@ -52,7 +52,7 @@ impl Connection<BufferedReader<NetStream>, BufferedWriter<NetStream>> { let socket = try!(TcpStream::connect(format!("{}:{}", host, port)[])); let ssl = try!(ssl_to_io(SslContext::new(SslMethod::Tlsv1))); let ssl_socket = try!(ssl_to_io(SslStream::new(&ssl, socket))); - Ok((BufferedReader::new(NetStream::SslTcpStream(ssl_socket.clone())), + Ok((BufferedReader::new(NetStream::SslTcpStream(ssl_socket.clone())), BufferedWriter::new(NetStream::SslTcpStream(ssl_socket)))) } @@ -78,13 +78,13 @@ impl Connection<BufferedReader<NetStream>, BufferedWriter<NetStream>> { *self.writer.lock() = writer; Ok(()) } - + /// Sets the keepalive for the network stream. #[experimental] pub fn set_keepalive(&self, delay_in_seconds: Option<uint>) -> IoResult<()> { self.mod_stream(|tcp| tcp.set_keepalive(delay_in_seconds)) } - + /// Sets the timeout for the network stream. #[experimental] pub fn set_timeout(&self, timeout_ms: Option<u64>) { @@ -114,7 +114,8 @@ impl<T: IrcReader, U: IrcWriter> Connection<T, U> { /// Sends a Message over this connection. #[experimental] #[cfg(feature = "encode")] - pub fn send(&self, message: Message, encoding: &str) -> IoResult<()> { + pub fn send<T: ToMessage>(&self, tomsg: T, encoding: &str) -> IoResult<()> { + let message = tomsg.to_message(); let encoding = match encoding_from_whatwg_label(encoding) { Some(enc) => enc, None => return Err(IoError { @@ -139,7 +140,8 @@ impl<T: IrcReader, U: IrcWriter> Connection<T, U> { /// Sends a message over this connection. #[experimental] #[cfg(not(feature = "encode"))] - pub fn send(&self, message: Message) -> IoResult<()> { + pub fn send<T: ToMessage>(&self, tomsg: T) -> IoResult<()> { + let message = tomsg.to_message(); let mut writer = self.writer.lock(); try!(writer.write_str(message.into_string()[])); writer.flush() @@ -181,7 +183,7 @@ impl<T: IrcReader, U: IrcWriter> Connection<T, U> { pub fn reader<'a>(&'a self) -> MutexGuard<'a, T> { self.reader.lock() } - + /// Acquires the Writer lock. #[experimental] pub fn writer<'a>(&'a self) -> MutexGuard<'a, U> { @@ -261,7 +263,7 @@ mod test { Message::new(None, "PRIVMSG", Some(vec!["test"]), Some("€ŠšŽžŒœŸ")), "UTF-8" ).is_ok()); let data = UTF_8.decode(conn.writer().get_ref(), DecoderTrap::Strict).unwrap(); - assert_eq!(data[], "PRIVMSG test :€ŠšŽžŒœŸ\r\n"); + assert_eq!(data[], "PRIVMSG test :€ŠšŽžŒœŸ\r\n"); } #[test] diff --git a/src/data/command.rs b/src/data/command.rs index a88f3b3..94ce31e 100644 --- a/src/data/command.rs +++ b/src/data/command.rs @@ -2,10 +2,10 @@ #![stable] use std::io::{InvalidInput, IoError, IoResult}; use std::str::FromStr; -use data::message::Message; +use data::message::{Message, ToMessage}; -/// List of all client commands as defined in [RFC 2812](http://tools.ietf.org/html/rfc2812). This -/// also includes commands from the +/// List of all client commands as defined in [RFC 2812](http://tools.ietf.org/html/rfc2812). This +/// also includes commands from the /// [capabilities extension](https://tools.ietf.org/html/draft-mitchell-irc-capabilities-01). /// Additionally, this includes some common additional commands from popular IRCds. #[stable] @@ -144,17 +144,18 @@ pub enum Command<'a> { HOSTSERV(&'a str), /// MEMOSERV message MEMOSERV(&'a str), - + // Capabilities extension to IRCv3 /// CAP COMMAND [param] CAP(CapSubCommand, Option<&'a str>), } -impl<'a> Command<'a> { +impl<'a> ToMessage for Command<'a> { + /// Converts a Command into a Message. #[stable] - pub fn to_message(self) -> Message { - match self { + fn to_message(&self) -> Message { + match *self { Command::PASS(p) => Message::new(None, "PASS", None, Some(p)), Command::NICK(n) => Message::new(None, "NICK", None, Some(n)), Command::USER(u, m, r) => Message::new(None, "USER", Some(vec![u, m, "*"]), Some(r)), @@ -162,7 +163,7 @@ impl<'a> Command<'a> { Command::MODE(t, m, Some(p)) => Message::new(None, "MODE", Some(vec![t, m, p]), None), Command::MODE(t, m, None) => Message::new(None, "MODE", Some(vec![t, m]), None), Command::SERVICE(n, r, d, t, re, i) => Message::new(None, "SERVICE", - Some(vec![n, r, d, t, re]), Some(i)), + Some(vec![n, r, d, t, re]), Some(i)), Command::QUIT(Some(m)) => Message::new(None, "QUIT", None, Some(m)), Command::QUIT(None) => Message::new(None, "QUIT", None, None), Command::SQUIT(s, c) => Message::new(None, "SQUIT", Some(vec![s]), Some(c)), @@ -186,7 +187,7 @@ impl<'a> Command<'a> { Command::MOTD(Some(t)) => Message::new(None, "MOTD", None, Some(t)), Command::MOTD(None) => Message::new(None, "MOTD", None, None), Command::LUSERS(Some(m), Some(t)) => Message::new(None, "LUSERS", Some(vec![m]), - Some(t)), + Some(t)), Command::LUSERS(Some(m), None) => Message::new(None, "LUSERS", Some(vec![m]), None), Command::LUSERS(None, _) => Message::new(None, "LUSERS", None, None), Command::VERSION(Some(t)) => Message::new(None, "VERSION", None, Some(t)), @@ -200,7 +201,7 @@ impl<'a> Command<'a> { Command::TIME(Some(t)) => Message::new(None, "TIME", None, Some(t)), Command::TIME(None) => Message::new(None, "TIME", None, None), Command::CONNECT(t, p, Some(r)) => Message::new(None, "CONNECT", Some(vec![t, p]), - Some(r)), + Some(r)), Command::CONNECT(t, p, None) => Message::new(None, "CONNECT", Some(vec![t, p]), None), Command::TRACE(Some(t)) => Message::new(None, "TRACE", None, Some(t)), Command::TRACE(None) => Message::new(None, "TRACE", None, None), @@ -209,20 +210,20 @@ impl<'a> Command<'a> { Command::INFO(Some(t)) => Message::new(None, "INFO", None, Some(t)), Command::INFO(None) => Message::new(None, "INFO", None, None), Command::SERVLIST(Some(m), Some(t)) => Message::new(None, "SERVLIST", Some(vec![m]), - Some(t)), + Some(t)), Command::SERVLIST(Some(m), None) => Message::new(None, "SERVLIST", Some(vec![m]), None), Command::SERVLIST(None, _) => Message::new(None, "SERVLIST", None, None), Command::SQUERY(s, t) => Message::new(None, "SQUERY", Some(vec![s, t]), None), Command::WHO(Some(s), Some(true)) => Message::new(None, "WHO", Some(vec![s, "o"]), - None), + None), Command::WHO(Some(s), _) => Message::new(None, "WHO", Some(vec![s]), None), Command::WHO(None, _) => Message::new(None, "WHO", None, None), Command::WHOIS(Some(t), m) => Message::new(None, "WHOIS", Some(vec![t, m]), None), Command::WHOIS(None, m) => Message::new(None, "WHOIS", Some(vec![m]), None), Command::WHOWAS(n, Some(c), Some(t)) => Message::new(None, "WHOWAS", Some(vec![n, c]), - Some(t)), + Some(t)), Command::WHOWAS(n, Some(c), None) => Message::new(None, "WHOWAS", Some(vec![n, c]), - None), + None), Command::WHOWAS(n, None, _) => Message::new(None, "WHOWAS", Some(vec![n]), None), Command::KILL(n, c) => Message::new(None, "KILL", Some(vec![n]), Some(c)), Command::PING(s, Some(t)) => Message::new(None, "PING", Some(vec![s]), Some(t)), @@ -236,18 +237,18 @@ impl<'a> Command<'a> { Command::DIE => Message::new(None, "DIE", None, None), Command::RESTART => Message::new(None, "RESTART", None, None), Command::SUMMON(u, Some(t), Some(c)) => Message::new(None, "SUMMON", Some(vec![u, t]), - Some(c)), + Some(c)), Command::SUMMON(u, Some(t), None) => Message::new(None, "SUMMON", Some(vec![u, t]), - None), + None), Command::SUMMON(u, None, _) => Message::new(None, "SUMMON", Some(vec![u]), None), Command::USERS(Some(t)) => Message::new(None, "USERS", None, Some(t)), Command::USERS(None) => Message::new(None, "USERS", None, None), Command::WALLOPS(t) => Message::new(None, "WALLOPS", None, Some(t)), - Command::USERHOST(u) => Message::new(None, "USERHOST", Some(u), None), - Command::ISON(u) => Message::new(None, "ISON", Some(u), None), + Command::USERHOST(ref u) => Message::new(None, "USERHOST", Some(u.clone()), None), + Command::ISON(ref u) => Message::new(None, "ISON", Some(u.clone()), None), Command::SAJOIN(n, c) => Message::new(None, "SAJOIN", Some(vec![n, c]), None), Command::SAMODE(t, m, Some(p)) => Message::new(None, "SAMODE", Some(vec![t, m, p]), - None), + None), Command::SAMODE(t, m, None) => Message::new(None, "SAMODE", Some(vec![t, m]), None), Command::SANICK(o, n) => Message::new(None, "SANICK", Some(vec![o, n]), None), Command::SAPART(c, r) => Message::new(None, "SAPART", Some(vec![c]), Some(r)), @@ -262,6 +263,9 @@ impl<'a> Command<'a> { } } +} + +impl<'a> Command<'a> { /// Converts a Message into a Command. #[stable] pub fn from_message(m: &'a Message) -> IoResult<Command<'a>> { @@ -981,7 +985,7 @@ impl CapSubCommand { } } -impl FromStr for CapSubCommand { +impl FromStr for CapSubCommand { fn from_str(s: &str) -> Option<CapSubCommand> { match s { "LS" => Some(CapSubCommand::LS), diff --git a/src/data/message.rs b/src/data/message.rs index 74de5d8..0669df2 100644 --- a/src/data/message.rs +++ b/src/data/message.rs @@ -3,6 +3,14 @@ use std::borrow::ToOwned; use std::str::FromStr; +/// Represents something that can be converted to a message. +pub trait ToMessage { + + /// Convert to message. + fn to_message(&self) -> Message; + +} + /// IRC Message data. #[experimental] #[deriving(Clone, PartialEq, Show)] @@ -54,6 +62,14 @@ impl Message { } } +impl ToMessage for Message { + + fn to_message(&self) -> Message { + self.clone() + } + +} + impl FromStr for Message { fn from_str(s: &str) -> Option<Message> { let mut state = s.clone(); diff --git a/src/server/mod.rs b/src/server/mod.rs index c014b66..1e55da9 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -8,6 +8,7 @@ use conn::{Connection, NetStream}; use data::{Command, Config, Message, Response, User}; use data::Command::{JOIN, NICK, NICKSERV, PONG}; use data::kinds::{IrcReader, IrcWriter}; +use data::message::ToMessage; #[cfg(feature = "ctcp")] use time::now; pub mod utils; @@ -42,7 +43,7 @@ pub struct IrcServer<T: IrcReader, U: IrcWriter> { pub type NetIrcServer = IrcServer<BufferedReader<NetStream>, BufferedWriter<NetStream>>; impl IrcServer<BufferedReader<NetStream>, BufferedWriter<NetStream>> { - /// Creates a new IRC Server connection from the configuration at the specified path, + /// Creates a new IRC Server connection from the configuration at the specified path, /// connecting immediately. #[experimental] pub fn new(config: &str) -> IoResult<NetIrcServer> { @@ -65,7 +66,7 @@ impl IrcServer<BufferedReader<NetStream>, BufferedWriter<NetStream>> { /// Reconnects to the IRC server. #[experimental] pub fn reconnect(&self) -> IoResult<()> { - self.conn.reconnect(self.config().server(), self.config.port()) + self.conn.reconnect(self.config().server(), self.config.port()) } } @@ -133,7 +134,7 @@ impl<T: IrcReader, U: IrcWriter> IrcServer<T, U> { for chan in self.config.channels().into_iter() { self.send(JOIN(chan[], None)).unwrap(); } - } else if resp == Response::ERR_NICKNAMEINUSE || + } else if resp == Response::ERR_NICKNAMEINUSE || resp == Response::ERR_ERRONEOUSNICKNAME { let alt_nicks = self.config.get_alternate_nicknames(); let mut index = self.alt_nick_index.write(); @@ -190,7 +191,7 @@ impl<T: IrcReader, U: IrcWriter> IrcServer<T, U> { let resp = if target.starts_with("#") { target[] } else { source }; match msg.suffix { Some(ref msg) if msg.starts_with("\u{001}") => { - let tokens: Vec<_> = { + let tokens: Vec<_> = { let end = if msg.ends_with("\u{001}") { msg.len() - 1 } else { @@ -199,9 +200,9 @@ impl<T: IrcReader, U: IrcWriter> IrcServer<T, U> { msg[1..end].split_str(" ").collect() }; println!("we made it this far."); - match tokens[0] { - "FINGER" => self.send_ctcp(resp, format!("FINGER :{} ({})", - self.config.real_name(), + match tokens[0] { + "FINGER" => self.send_ctcp(resp, format!("FINGER :{} ({})", + self.config.real_name(), self.config.username())[]), "VERSION" => self.send_ctcp(resp, "VERSION irc:git:Rust"), "SOURCE" => { @@ -212,7 +213,7 @@ impl<T: IrcReader, U: IrcWriter> IrcServer<T, U> { }, "PING" => self.send_ctcp(resp, format!("PING {}", tokens[1])[]), "TIME" => self.send_ctcp(resp, format!("TIME :{}", now().rfc822z())[]), - "USERINFO" => self.send_ctcp(resp, format!("USERINFO :{}", + "USERINFO" => self.send_ctcp(resp, format!("USERINFO :{}", self.config.user_info())[]), _ => {} } @@ -276,7 +277,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Iterator<IoResult<Message>> for ServerItera desc: "Failed to parse message.", detail: Some(msg) }) - } + } ); match res { Err(ref err) if err.kind == IoErrorKind::EndOfFile => None, @@ -451,7 +452,7 @@ mod test { // but ignores the ordering of these entries. let mut levels = server.list_users("#test").unwrap()[0].access_levels(); levels.retain(|l| exp.access_levels().contains(l)); - assert_eq!(levels.len(), exp.access_levels().len()); + assert_eq!(levels.len(), exp.access_levels().len()); } #[test] @@ -492,7 +493,7 @@ mod test { for message in server.iter() { println!("{}", message); } - assert_eq!(get_server_value(server)[], + assert_eq!(get_server_value(server)[], "NOTICE test :\u{001}SOURCE https://github.com/aatxe/irc\u{001}\r\n\ NOTICE test :\u{001}SOURCE\u{001}\r\n"); }