From b2006d044d5d8d91e76484b56098d0cdaedd48b4 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Sun, 2 Nov 2014 16:25:57 -0500 Subject: [PATCH 01/18] Laid out basic structure of rewritten library. --- Cargo.toml | 3 -- examples/simple.rs | 27 ------------ mktestconfig.sh | 1 - src/bot.rs | 69 ----------------------------- src/conn.rs | 51 ---------------------- src/data.rs | 106 --------------------------------------------- src/lib.rs | 70 +++--------------------------- src/server.rs | 0 src/utils.rs | 53 +++++++++++++++++++++++ 9 files changed, 60 insertions(+), 320 deletions(-) delete mode 100644 examples/simple.rs delete mode 100644 mktestconfig.sh delete mode 100644 src/bot.rs delete mode 100644 src/data.rs create mode 100644 src/server.rs create mode 100644 src/utils.rs diff --git a/Cargo.toml b/Cargo.toml index 83f1052..c538663 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,3 @@ name = "irc" version = "0.0.1" authors = ["Aaron Weiss "] - -[[example]] -name = "simple" diff --git a/examples/simple.rs b/examples/simple.rs deleted file mode 100644 index 566a78c..0000000 --- a/examples/simple.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![feature(slicing_syntax)] -extern crate irc; - -use std::collections::HashMap; -use irc::Server; -use irc::bot::IrcServer; -use irc::data::{Config, Message}; - -fn main() { - let config = Config { - owners: vec!("awe".into_string()), - nickname: "pickles".into_string(), - username: "pickles".into_string(), - realname: "pickles".into_string(), - password: "".into_string(), - server: "irc.fyrechat.net".into_string(), - port: 6667, - channels: vec!("#vana".into_string()), - options: HashMap::new(), - }; - let mut server = IrcServer::new_with_config(config).unwrap(); - server.send(Message::new(None, "NICK", vec!["pickles"], None)).unwrap(); - server.send(Message::new(None, "USER", vec!["pickles", "0", "*", "pickles"], None)).unwrap(); - for message in server { - println!("RCV: {}", message); - } -} diff --git a/mktestconfig.sh b/mktestconfig.sh deleted file mode 100644 index c650ad8..0000000 --- a/mktestconfig.sh +++ /dev/null @@ -1 +0,0 @@ -echo "{\"owners\": [\"test\"],\"nickname\": \"test\",\"username\": \"test\",\"realname\": \"test\",\"password\": \"\",\"server\": \"irc.fyrechat.net\",\"port\": 6667,\"channels\": [\"#test\", \"#test2\"],\"options\": {}}" > config.json diff --git a/src/bot.rs b/src/bot.rs deleted file mode 100644 index 4e1e015..0000000 --- a/src/bot.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::cell::RefCell; -use std::collections::HashMap; -use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; -use {Server, process}; -use conn::Connection; -use data::{Config, IrcReader, IrcWriter, Message, User}; - -pub struct IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { - pub conn: Connection, - pub config: Config, - chanlists: RefCell>>, -} - -impl<'a> IrcServer<'a, BufferedWriter, BufferedReader> { - pub fn new() -> IoResult, BufferedReader>> { - let config = try!(Config::load_utf8("config.json")); - let conn = try!(Connection::connect(config.server[], config.port)); - Ok(IrcServer { - conn: conn, - config: config, - chanlists: RefCell::new(HashMap::new()), - }) - } - - pub fn new_with_config(config: Config) -> IoResult, BufferedReader>> { - let conn = try!(Connection::connect(config.server[], config.port)); - Ok(IrcServer { - conn: conn, - config: config, - chanlists: RefCell::new(HashMap::new()), - }) - } -} - -impl<'a, T, U> Iterator for IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { - fn next(&mut self) -> Option { - let line_res = self.conn.reader().read_line(); - if let Err(e) = line_res { println!("{}", e); return None; } - let line = line_res.unwrap(); - let processed = process(line[]); - if let Err(e) = processed { println!("{}", e); return None; } - let (source, command, args) = processed.unwrap(); - Some(Message::new(Some(source), command, args, None)) - } -} - -impl<'a, T, U> Server<'a> for IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { - fn send(&self, message: Message) -> IoResult<()> { - self.conn.send(message) - } - - fn config(&self) -> &Config { - &self.config - } - - fn get_users(&self, chan: &str) -> Option> { - self.chanlists.borrow_mut().find_copy(&chan.into_string()) - } -} - -impl<'a, T, U> IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { - pub fn from_connection(conn: Connection) -> IoResult> { - Ok(IrcServer { - conn: conn, - config: try!(Config::load_utf8("config.json")), - chanlists: RefCell::new(HashMap::new()), - }) - } -} diff --git a/src/conn.rs b/src/conn.rs index 011b4d5..e69de29 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -1,51 +0,0 @@ -use std::cell::{RefCell, RefMut}; -use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream, Writer}; -use data::{IrcReader, IrcWriter, Message}; - -pub struct Connection where T: IrcWriter, U: IrcReader { - writer: RefCell, - reader: RefCell, -} - -impl Connection, BufferedReader> { - pub fn connect(host: &str, port: u16) -> IoResult, BufferedReader>> { - let socket = try!(TcpStream::connect(host, port)); - Connection::new(BufferedWriter::new(socket.clone()), BufferedReader::new(socket.clone())) - } -} - -impl Connection where T: IrcWriter, U: IrcReader { - pub fn new(writer: T, reader: U) -> IoResult> { - Ok(Connection { - writer: RefCell::new(writer), - reader: RefCell::new(reader), - }) - } - - fn send_internal(&self, msg: &str) -> IoResult<()> { - let mut send = self.writer.borrow_mut(); - try!(send.write_str(msg)); - send.flush() - } - - pub fn send(&self, msg: Message) -> IoResult<()> { - let mut send = msg.command.to_string(); - if msg.args.init().len() > 0 { - send.push_str(" "); - send.push_str(msg.args.init().connect(" ")[]); - } - send.push_str(" "); - if msg.colon_flag.is_some() { send.push_str(":") } - send.push_str(msg.args.last().unwrap()[]); - send.push_str("\r\n"); - self.send_internal(send[]) - } - - pub fn writer<'a>(&'a self) -> RefMut<'a, T> { - self.writer.borrow_mut() - } - - pub fn reader<'a>(&'a self) -> RefMut<'a, U> { - self.reader.borrow_mut() - } -} diff --git a/src/data.rs b/src/data.rs deleted file mode 100644 index 872f1d5..0000000 --- a/src/data.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::collections::HashMap; -use std::io::fs::File; -use std::io::{InvalidInput, IoError, IoResult}; -use serialize::json::{decode}; - -pub trait IrcWriter: Writer + Sized + 'static {} -impl IrcWriter for T where T: Writer + Sized + 'static {} -pub trait IrcReader: Buffer + Sized + 'static {} -impl IrcReader for T where T: Buffer + Sized + 'static {} - -#[deriving(PartialEq, Clone, Show)] -pub struct User { - name: String, - access_level: AccessLevel, -} - -impl User { - pub fn new(name: &str) -> User { - let rank = AccessLevel::from_str(name); - User { - name: if let Member = rank { - name.into_string() - } else { - name[1..].into_string() - }, - access_level: rank, - } - } -} - -#[deriving(PartialEq, Clone, Show)] -pub enum AccessLevel { - Owner, - Admin, - Oper, - HalfOp, - Voice, - Member, -} - -impl AccessLevel { - pub fn from_str(s: &str) -> AccessLevel { - if s.len() == 0 { Member } else { - match s.char_at(0) { - '~' => Owner, - '&' => Admin, - '@' => Oper, - '%' => HalfOp, - '+' => Voice, - _ => Member, - } - } - } -} - -#[deriving(Show, PartialEq)] -pub struct Message { - pub source: Option, - pub command: String, - pub args: Vec, - pub colon_flag: Option, -} - -impl<'a> Message { - pub fn new(source: Option<&'a str>, command: &'a str, args: Vec<&'a str>, colon_flag: Option) -> Message { - Message { - source: source.map(|s: &str| s.into_string()), - command: command.into_string(), - args: args.into_iter().map(|s: &str| s.into_string()).collect(), - colon_flag: colon_flag, - } - } -} - -#[deriving(Clone, Decodable)] -pub struct Config { - pub owners: Vec, - pub nickname: String, - pub username: String, - pub realname: String, - pub password: String, - pub server: String, - pub port: u16, - pub channels: Vec, - pub options: HashMap, -} - -impl Config { - pub fn load(path: Path) -> IoResult { - let mut file = try!(File::open(&path)); - let data = try!(file.read_to_string()); - decode(data[]).map_err(|e| IoError { - kind: InvalidInput, - desc: "Decoder error", - detail: Some(e.to_string()), - }) - } - - pub fn load_utf8(path: &str) -> IoResult { - Config::load(Path::new(path)) - } - - pub fn is_owner(&self, nickname: &str) -> bool { - self.owners[].contains(&String::from_str(nickname)) - } -} diff --git a/src/lib.rs b/src/lib.rs index e2a53ce..7e3d85e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,7 @@ +//! A simple, thread-safe IRC client library. +#![crate_name = "irc"] +#![crate_type = "lib"] + #![feature(if_let)] #![feature(phase)] #![feature(slicing_syntax)] @@ -5,66 +9,6 @@ extern crate regex; #[phase(plugin)] extern crate regex_macros; extern crate serialize; -use std::io::{InvalidInput, IoError, IoResult}; - -pub mod bot; -pub mod conn; -pub mod data; - -pub trait Server<'a>: Iterator { - fn send(&self, message: data::Message) -> IoResult<()>; - fn config(&self) -> &data::Config; - fn get_users(&self, chan: &str) -> Option>; -} - -fn process(msg: &str) -> IoResult<(&str, &str, Vec<&str>)> { - let reg = regex!(r"^(?::([^ ]+) )?([^ ]+)(.*)"); - let cap = match reg.captures(msg) { - Some(x) => x, - None => return Err(IoError { - kind: InvalidInput, - desc: "Failed to parse line", - detail: None, - }), - }; - let source = cap.at(1); - let command = cap.at(2); - let args = parse_args(cap.at(3)); - Ok((source, command, args)) -} - -fn parse_args(line: &str) -> Vec<&str> { - let reg = regex!(r" ([^: ]+)| :([^\r\n]*)[\r\n]*$"); - reg.captures_iter(line).map(|cap| { - match cap.at(1) { - "" => cap.at(2), - x => x, - } - }).collect() -} - -#[cfg(test)] -mod test { - use super::{process, parse_args}; - - #[test] - fn process_line() { - let res = process(":flare.to.ca.fyrechat.net 353 pickles = #pickles :pickles awe\r\n").unwrap(); - let (source, command, args) = res; - assert_eq!(source, "flare.to.ca.fyrechat.net"); - assert_eq!(command, "353"); - assert_eq!(args, vec!["pickles", "=", "#pickles", "pickles awe"]); - - let res = process("PING :flare.to.ca.fyrechat.net\r\n").unwrap(); - let (source, command, args) = res; - assert_eq!(source, ""); - assert_eq!(command, "PING"); - assert_eq!(args, vec!["flare.to.ca.fyrechat.net"]); - } - - #[test] - fn process_args() { - let res = parse_args("PRIVMSG #vana :hi"); - assert_eq!(res, vec!["#vana", "hi"]) - } -} +mod conn; +pub mod server; +mod utils; diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..324e70c --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,53 @@ +use std::io::{InvalidInput, IoError, IoResult}; + +fn process(msg: &str) -> IoResult<(&str, &str, Vec<&str>)> { + let reg = regex!(r"^(?::([^ ]+) )?([^ ]+)(.*)"); + let cap = match reg.captures(msg) { + Some(x) => x, + None => return Err(IoError { + kind: InvalidInput, + desc: "Failed to parse line", + detail: None, + }), + }; + let source = cap.at(1); + let command = cap.at(2); + let args = parse_args(cap.at(3)); + Ok((source, command, args)) +} + +fn parse_args(line: &str) -> Vec<&str> { + let reg = regex!(r" ([^: ]+)| :([^\r\n]*)[\r\n]*$"); + reg.captures_iter(line).map(|cap| { + match cap.at(1) { + "" => cap.at(2), + x => x, + } + }).collect() +} + +#[cfg(test)] +mod test { + use super::{process, parse_args}; + + #[test] + fn process_line() { + let res = process(":flare.to.ca.fyrechat.net 353 pickles = #pickles :pickles awe\r\n").unwrap(); + let (source, command, args) = res; + assert_eq!(source, "flare.to.ca.fyrechat.net"); + assert_eq!(command, "353"); + assert_eq!(args, vec!["pickles", "=", "#pickles", "pickles awe"]); + + let res = process("PING :flare.to.ca.fyrechat.net\r\n").unwrap(); + let (source, command, args) = res; + assert_eq!(source, ""); + assert_eq!(command, "PING"); + assert_eq!(args, vec!["flare.to.ca.fyrechat.net"]); + } + + #[test] + fn process_args() { + let res = parse_args("PRIVMSG #vana :hi"); + assert_eq!(res, vec!["#vana", "hi"]) + } +} From 6da40f2ad3b8df227d23dd4b28589090efebff08 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Sun, 2 Nov 2014 17:25:45 -0500 Subject: [PATCH 02/18] Implemented thread-safe Connection. --- src/conn.rs | 35 ++++++++++++++++++++++++++++++ src/data/message.rs | 38 ++++++++++++++++++++++++++++++++ src/data/mod.rs | 8 +++++++ src/lib.rs | 7 ++---- src/utils.rs | 53 --------------------------------------------- 5 files changed, 83 insertions(+), 58 deletions(-) create mode 100644 src/data/message.rs create mode 100644 src/data/mod.rs delete mode 100644 src/utils.rs diff --git a/src/conn.rs b/src/conn.rs index e69de29..d31f3ea 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -0,0 +1,35 @@ +use std::sync::Mutex; +use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; +use data::kinds::{IrcWriter, IrcReader}; +use data::message::Message; + +pub struct Connection where T: IrcWriter, U: IrcReader { + writer: Mutex, + reader: Mutex, +} + +impl Connection, BufferedReader> { + pub fn connect(host: &str, port: u16) -> IoResult, BufferedReader>> { + let socket = try!(TcpStream::connect(host, port)); + Ok(Connection::new(BufferedWriter::new(socket.clone()), BufferedReader::new(socket))) + } +} + +impl Connection where T: IrcWriter, U: IrcReader { + pub fn new(writer: T, reader: U) -> Connection { + Connection { + writer: Mutex::new(writer), + reader: Mutex::new(reader), + } + } + + pub fn send(&self, msg: Message) -> IoResult<()> { + let mut send = self.writer.lock(); + try!(send.write_str(msg.into_string()[])); + send.flush() + } + + pub fn recv(&self) -> IoResult { + self.reader.lock().read_line() + } +} diff --git a/src/data/message.rs b/src/data/message.rs new file mode 100644 index 0000000..fd3ed90 --- /dev/null +++ b/src/data/message.rs @@ -0,0 +1,38 @@ +#[deriving(Clone, PartialEq, Show)] +pub struct Message { + pub prefix: Option, + pub command: String, + pub args: Vec, + pub suffix: Option, +} + +impl Message { + pub fn new(prefix: Option<&str>, command: &str, args: Option>, suffix: Option<&str>) + -> Message { + Message { + prefix: prefix.map(|s| s.into_string()), + command: command.into_string(), + args: args.map_or(Vec::new(), |v| v.iter().map(|s| s.into_string()).collect()), + suffix: suffix.map(|s| s.into_string()), + } + } + + pub fn into_string(&self) -> String { + let mut ret = String::new(); + if let Some(ref prefix) = self.prefix { + ret.push(':'); + ret.push_str(prefix[]); + ret.push(' '); + } + ret.push_str(self.command[]); + for arg in self.args.iter() { + ret.push(' '); + ret.push_str(arg[]); + } + if let Some(ref suffix) = self.suffix { + ret.push_str(" :"); + ret.push_str(suffix[]); + } + ret + } +} diff --git a/src/data/mod.rs b/src/data/mod.rs new file mode 100644 index 0000000..3178a28 --- /dev/null +++ b/src/data/mod.rs @@ -0,0 +1,8 @@ +pub mod kinds { + pub trait IrcWriter: Writer + Sized + Send + 'static {} + impl IrcWriter for T where T: Writer + Sized + Send + 'static {} + pub trait IrcReader: Buffer + Sized + Send + 'static {} + impl IrcReader for T where T: Buffer + Sized + Send + 'static {} +} + +pub mod message; diff --git a/src/lib.rs b/src/lib.rs index 7e3d85e..2de2166 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,12 +3,9 @@ #![crate_type = "lib"] #![feature(if_let)] -#![feature(phase)] #![feature(slicing_syntax)] -extern crate regex; -#[phase(plugin)] extern crate regex_macros; extern crate serialize; -mod conn; + mod conn; +pub mod data; pub mod server; -mod utils; diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 324e70c..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::io::{InvalidInput, IoError, IoResult}; - -fn process(msg: &str) -> IoResult<(&str, &str, Vec<&str>)> { - let reg = regex!(r"^(?::([^ ]+) )?([^ ]+)(.*)"); - let cap = match reg.captures(msg) { - Some(x) => x, - None => return Err(IoError { - kind: InvalidInput, - desc: "Failed to parse line", - detail: None, - }), - }; - let source = cap.at(1); - let command = cap.at(2); - let args = parse_args(cap.at(3)); - Ok((source, command, args)) -} - -fn parse_args(line: &str) -> Vec<&str> { - let reg = regex!(r" ([^: ]+)| :([^\r\n]*)[\r\n]*$"); - reg.captures_iter(line).map(|cap| { - match cap.at(1) { - "" => cap.at(2), - x => x, - } - }).collect() -} - -#[cfg(test)] -mod test { - use super::{process, parse_args}; - - #[test] - fn process_line() { - let res = process(":flare.to.ca.fyrechat.net 353 pickles = #pickles :pickles awe\r\n").unwrap(); - let (source, command, args) = res; - assert_eq!(source, "flare.to.ca.fyrechat.net"); - assert_eq!(command, "353"); - assert_eq!(args, vec!["pickles", "=", "#pickles", "pickles awe"]); - - let res = process("PING :flare.to.ca.fyrechat.net\r\n").unwrap(); - let (source, command, args) = res; - assert_eq!(source, ""); - assert_eq!(command, "PING"); - assert_eq!(args, vec!["flare.to.ca.fyrechat.net"]); - } - - #[test] - fn process_args() { - let res = parse_args("PRIVMSG #vana :hi"); - assert_eq!(res, vec!["#vana", "hi"]) - } -} From d97035cca9527ee6b53e31c2c59c6650096b982d Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Sun, 2 Nov 2014 18:08:56 -0500 Subject: [PATCH 03/18] Implemented ServerIterator and message parsing. --- src/data/message.rs | 31 +++++++++++++++++++++++++++++++ src/server.rs | 19 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/data/message.rs b/src/data/message.rs index fd3ed90..9665cd5 100644 --- a/src/data/message.rs +++ b/src/data/message.rs @@ -1,3 +1,5 @@ +use std::from_str::FromStr; + #[deriving(Clone, PartialEq, Show)] pub struct Message { pub prefix: Option, @@ -36,3 +38,32 @@ impl Message { ret } } + +impl FromStr for Message { + fn from_str(s: &str) -> Option { + let mut state = s.clone(); + if s.len() == 0 { return None } + let prefix = if state.starts_with(":") { + let prefix = state.find(' ').map(|i| s[1..i]); + state = state.find(' ').map_or("", |i| s[i..]); + prefix + } else { + None + }; + let suffix = if state.contains(":") { + let suffix = state.find(':').map(|i| s[i..]); + state = state.find(':').map_or("", |i| s[..i]); + suffix + } else { + None + }; + let command = if let Some(cmd) = state.find(' ').map(|i| s[..i]) { + state = state.find(' ').map_or("", |i| s[i..]); + cmd + } else { + return None + }; + let args: Vec<_> = state.splitn(14, ' ').collect(); + Some(Message::new(prefix, command, if args.len() > 0 { Some(args) } else { None }, suffix)) + } +} diff --git a/src/server.rs b/src/server.rs index e69de29..95c3905 100644 --- a/src/server.rs +++ b/src/server.rs @@ -0,0 +1,19 @@ +use conn::Connection; +use data::kinds::{IrcReader, IrcWriter}; +use data::message::Message; + +pub struct IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { + pub conn: Connection +} + +pub struct ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader { + pub server: &'a IrcServer<'a, T, U> +} + +impl<'a, T, U> Iterator for ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader { + fn next(&mut self) -> Option { + let line = self.server.conn.recv(); + if let Err(_) = line { return None } + from_str(line.unwrap()[]) + } +} From 4df7be1662eec4d9618f5d58dfacaed781eb255e Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Sun, 2 Nov 2014 18:16:49 -0500 Subject: [PATCH 04/18] Added Config implementation. --- src/data/config.rs | 37 +++++++++++++++++++++++++++++++++++++ src/data/mod.rs | 1 + 2 files changed, 38 insertions(+) create mode 100644 src/data/config.rs diff --git a/src/data/config.rs b/src/data/config.rs new file mode 100644 index 0000000..5fd3d0d --- /dev/null +++ b/src/data/config.rs @@ -0,0 +1,37 @@ +use std::collections::HashMap; +use std::io::fs::File; +use std::io::{InvalidInput, IoError, IoResult}; +use serialize::json::decode; + +#[deriving(Clone, Decodable)] +pub struct Config { + pub owners: Vec, + pub nickname: String, + pub username: String, + pub realname: String, + pub password: String, + pub server: String, + pub port: u16, + pub channels: Vec, + pub options: HashMap, +} + +impl Config { + pub fn load(path: Path) -> IoResult { + let mut file = try!(File::open(&path)); + let data = try!(file.read_to_string()); + decode(data[]).map_err(|e| IoError { + kind: InvalidInput, + desc: "Failed to decode configuration file.", + detail: Some(e.to_string()), + }) + } + + pub fn load_utf8(path: &str) -> IoResult { + Config::load(Path::new(path)) + } + + pub fn is_owner(&self, nickname: &str) -> bool { + self.owners[].contains(&String::from_str(nickname)) + } +} diff --git a/src/data/mod.rs b/src/data/mod.rs index 3178a28..471a5ed 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -5,4 +5,5 @@ pub mod kinds { impl IrcReader for T where T: Buffer + Sized + Send + 'static {} } +pub mod config; pub mod message; From 2e2d411907600333fae5d45bc86dc7a6a49e264d Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Sun, 2 Nov 2014 18:39:00 -0500 Subject: [PATCH 05/18] Added Server trait and implemented basic Server functionality for IrcServer. --- src/conn.rs | 4 ++-- src/server.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/conn.rs b/src/conn.rs index d31f3ea..9961f7a 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -23,9 +23,9 @@ impl Connection where T: IrcWriter, U: IrcReader { } } - pub fn send(&self, msg: Message) -> IoResult<()> { + pub fn send(&self, message: Message) -> IoResult<()> { let mut send = self.writer.lock(); - try!(send.write_str(msg.into_string()[])); + try!(send.write_str(message.into_string()[])); send.flush() } diff --git a/src/server.rs b/src/server.rs index 95c3905..425c942 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,15 +1,61 @@ +use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; use conn::Connection; +use data::config::Config; use data::kinds::{IrcReader, IrcWriter}; use data::message::Message; +pub trait Server<'a, T, U> { + fn send(&self, _: Message) -> IoResult<()>; + fn iter(&'a self) -> ServerIterator<'a, T, U>; +} + pub struct IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { - pub conn: Connection + pub conn: Connection, + pub config: Config +} + +impl<'a> IrcServer<'a, BufferedWriter, BufferedReader> { + pub fn new(config: &str) -> IoResult, BufferedReader>> { + let config = try!(Config::load_utf8(config)); + let conn = try!(Connection::connect(config.server[], config.port)); + Ok(IrcServer { + conn: conn, + config: config + }) + } +} + +impl<'a, T, U> Server<'a, T, U> for IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader{ + fn send(&self, message: Message) -> IoResult<()> { + self.conn.send(message) + } + + fn iter(&'a self) -> ServerIterator<'a, T, U> { + ServerIterator::new(self) + } +} + +impl<'a, T, U> IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { + pub fn from_connection(config: &str, conn: Connection) -> IoResult> { + Ok(IrcServer { + conn: conn, + config: try!(Config::load_utf8(config)) + }) + } } pub struct ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader { pub server: &'a IrcServer<'a, T, U> } +impl<'a, T, U> ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader { + pub fn new(server: &'a IrcServer<'a, T, U>) -> ServerIterator<'a, T, U> { + ServerIterator { + server: server + } + } +} + impl<'a, T, U> Iterator for ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader { fn next(&mut self) -> Option { let line = self.server.conn.recv(); From 4ace08da3c2b216b1a6ea0a2f3e83e0af85a1368 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Sun, 2 Nov 2014 18:45:29 -0500 Subject: [PATCH 06/18] Took non-existant script out of travis build configuration. --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6eae4d7..b284ea8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,5 @@ language: rust script: - - chmod +x mktestconfig.sh - - ./mktestconfig.sh - cargo build --verbose - cargo test --verbose - cargo doc --verbose From 63f4ca509775b056f80a05108810f8fc7a10b4ff Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Mon, 3 Nov 2014 00:08:40 -0500 Subject: [PATCH 07/18] Implemented complete RFC 2812 as Commands. --- src/data/command.rs | 747 ++++++++++++++++++++++++++++++++++++++++++++ src/data/mod.rs | 1 + src/server.rs | 7 +- 3 files changed, 752 insertions(+), 3 deletions(-) create mode 100644 src/data/command.rs diff --git a/src/data/command.rs b/src/data/command.rs new file mode 100644 index 0000000..2c15378 --- /dev/null +++ b/src/data/command.rs @@ -0,0 +1,747 @@ +use std::io::{InvalidInput, IoError, IoResult}; +use data::message::Message; + +/// List of all client commands as defined in RFC 2812. +#[deriving(Show, PartialEq)] +pub enum Command { + // 3.1 Connection Registration + /// PASS + PASS(String), + /// NICK + NICK(String), + /// USER * + USER(String, String, String), + /// OPER + OPER(String, String), + /// MODE + /// MODE [] + MODE(String, String, Option), + /// SERVICE + SERVICE(String, String, String, String, String, String), + /// QUIT + QUIT(Option), + /// SQUIT + SQUIT(String, String), + + // 3.2 Channel operations + /// JOIN [] + JOIN(String, Option), + /// PART [] + PART(String, Option), + // MODE is already defined. + // MODE(String, String, Option), + /// TOPIC [] + TOPIC(String, Option), + /// NAMES [ []] + NAMES(Option, Option), + /// LIST [ []] + LIST(Option, Option), + /// INVITE + INVITE(String, String), + /// KICK [] + KICK(String, String, Option), + + // 3.3 Sending messages + /// PRIVMSG + PRIVMSG(String, String), + /// NOTICE + NOTICE(String, String), + + // 3.4 Server queries and commands + /// MOTD [] + MOTD(Option), + /// LUSERS [ []] + LUSERS(Option, Option), + /// VERSION [] + VERSION(Option), + /// STATS [ []] + STATS(Option, Option), + /// LINKS [[] ] + LINKS(Option, Option), + /// TIME [] + TIME(Option), + /// CONNECT [] + CONNECT(String, String, Option), + /// TRACE [] + TRACE(Option), + /// ADMIN [] + ADMIN(Option), + /// INFO [] + INFO(Option), + + // 3.5 Service Query and Commands + /// SERVLIST [ []] + SERVLIST(Option, Option), + /// SQUERY + SQUERY(String, String), + + // 3.6 User based queries + /// WHO [ ["o"]] + WHO(Option, Option), + /// WHOIS [] + WHOIS(Option, String), + /// WHOWAS [ []] + WHOWAS(String, Option, Option), + + // 3.7 Miscellaneous messages + /// KILL + KILL(String, String), + /// PING [] + PING(String, Option), + /// PONG [] + PONG(String, Option), + /// ERROR + ERROR(String), + + + // 4 Optional Features + /// AWAY [] + AWAY(Option), + /// REHASH + REHASH, + /// DIE + DIE, + /// RESTART + RESTART, + /// SUMMON [ []] + SUMMON(String, Option, Option), + /// USERS [] + USERS(Option), + /// WALLOPS + WALLOPS(String), + /// USERHOST + USERHOST(Vec), + /// ISON + ISON(Vec), + + // Non-RFC commands from InspIRCd + /// SAJOIN + SAJOIN(String, String), + /// SAMODE [] + SAMODE(String, String, Option), + /// SANICK + SANICK(String, String), + /// SAPART + SAPART(String, String), + /// SAQUIT + SAQUIT(String, String), +} + +impl Command { + pub fn to_message(self) -> Message { + match self { + PASS(p) => Message::new(None, "PASS", None, Some(p[])), + NICK(n) => Message::new(None, "NICK", None, Some(n[])), + USER(u, m, r) => Message::new(None, "USER", Some(vec![u[], m[], "*"]), Some(r[])), + OPER(u, p) => Message::new(None, "OPER", Some(vec![u[]]), Some(p[])), + MODE(t, m, Some(p)) => Message::new(None, "MODE", Some(vec![t[], m[], p[]]), None), + MODE(t, m, None) => Message::new(None, "MODE", Some(vec![t[], m[]]), None), + SERVICE(n, r, d, t, re, i) => Message::new(None, "SERVICE", + Some(vec![n[], r[], d[], t[], re[]]), Some(i[])), + QUIT(Some(m)) => Message::new(None, "QUIT", None, Some(m[])), + QUIT(None) => Message::new(None, "QUIT", None, None), + SQUIT(s, c) => Message::new(None, "SQUIT", Some(vec![s[]]), Some(c[])), + JOIN(c, Some(k)) => Message::new(None, "JOIN", Some(vec![c[], k[]]), None), + JOIN(c, None) => Message::new(None, "JOIN", Some(vec![c[]]), None), + PART(c, Some(m)) => Message::new(None, "PART", Some(vec![c[]]), Some(m[])), + PART(c, None) => Message::new(None, "PART", Some(vec![c[]]), None), + TOPIC(c, Some(t)) => Message::new(None, "TOPIC", Some(vec![c[]]), Some(t[])), + TOPIC(c, None) => Message::new(None, "TOPIC", Some(vec![c[]]), None), + NAMES(Some(c), Some(t)) => Message::new(None, "NAMES", Some(vec![c[]]), Some(t[])), + NAMES(Some(c), None) => Message::new(None, "NAMES", Some(vec![c[]]), None), + NAMES(None, _) => Message::new(None, "NAMES", None, None), + LIST(Some(c), Some(t)) => Message::new(None, "LIST", Some(vec![c[]]), Some(t[])), + LIST(Some(c), None) => Message::new(None, "LIST", Some(vec![c[]]), None), + LIST(None, _) => Message::new(None, "LIST", None, None), + INVITE(n, c) => Message::new(None, "INVITE", Some(vec![n[], c[]]), None), + KICK(c, n, Some(r)) => Message::new(None, "KICK", Some(vec![c[], n[]]), Some(r[])), + KICK(c, n, None) => Message::new(None, "KICK", Some(vec![c[], n[]]), None), + PRIVMSG(t, m) => Message::new(None, "PRIVMSG", Some(vec![t[]]), Some(m[])), + NOTICE(t, m) => Message::new(None, "NOTICE", Some(vec![t[]]), Some(m[])), + MOTD(Some(t)) => Message::new(None, "MOTD", None, Some(t[])), + MOTD(None) => Message::new(None, "MOTD", None, None), + LUSERS(Some(m), Some(t)) => Message::new(None, "LUSERS", Some(vec![m[]]), Some(t[])), + LUSERS(Some(m), None) => Message::new(None, "LUSERS", Some(vec![m[]]), None), + LUSERS(None, _) => Message::new(None, "LUSERS", None, None), + VERSION(Some(t)) => Message::new(None, "VERSION", None, Some(t[])), + VERSION(None) => Message::new(None, "VERSION", None, None), + STATS(Some(q), Some(t)) => Message::new(None, "STATS", Some(vec![q[]]), Some(t[])), + STATS(Some(q), None) => Message::new(None, "STATS", Some(vec![q[]]), None), + STATS(None, _) => Message::new(None, "STATS", None, None), + LINKS(Some(r), Some(s)) => Message::new(None, "LINKS", Some(vec![r[]]), Some(s[])), + LINKS(None, Some(s)) => Message::new(None, "LINKS", None, Some(s[])), + LINKS(_, None) => Message::new(None, "LINKS", None, None), + TIME(Some(t)) => Message::new(None, "TIME", None, Some(t[])), + TIME(None) => Message::new(None, "TIME", None, None), + CONNECT(t, p, Some(r)) => Message::new(None, "CONNECT", Some(vec![t[], p[]]), Some(r[])), + CONNECT(t, p, None) => Message::new(None, "CONNECT", Some(vec![t[], p[]]), None), + TRACE(Some(t)) => Message::new(None, "TRACE", None, Some(t[])), + TRACE(None) => Message::new(None, "TRACE", None, None), + ADMIN(Some(t)) => Message::new(None, "ADMIN", None, Some(t[])), + ADMIN(None) => Message::new(None, "ADMIN", None, None), + INFO(Some(t)) => Message::new(None, "INFO", None, Some(t[])), + INFO(None) => Message::new(None, "INFO", None, None), + SERVLIST(Some(m), Some(t)) => Message::new(None, "SERVLIST", Some(vec![m[]]), Some(t[])), + SERVLIST(Some(m), None) => Message::new(None, "SERVLIST", Some(vec![m[]]), None), + SERVLIST(None, _) => Message::new(None, "SERVLIST", None, None), + SQUERY(s, t) => Message::new(None, "SQUERY", Some(vec![s[], t[]]), None), + WHO(Some(s), Some(true)) => Message::new(None, "WHO", Some(vec![s[], "o"]), None), + WHO(Some(s), _) => Message::new(None, "WHO", Some(vec![s[]]), None), + WHO(None, _) => Message::new(None, "WHO", None, None), + WHOIS(Some(t), m) => Message::new(None, "WHOIS", Some(vec![t[], m[]]), None), + WHOIS(None, m) => Message::new(None, "WHOIS", Some(vec![m[]]), None), + WHOWAS(n, Some(c), Some(t)) => Message::new(None, "WHOWAS", Some(vec![n[], c[]]), Some(t[])), + WHOWAS(n, Some(c), None) => Message::new(None, "WHOWAS", Some(vec![n[], c[]]), None), + WHOWAS(n, None, _) => Message::new(None, "WHOWAS", Some(vec![n[]]), None), + KILL(n, c) => Message::new(None, "KILL", Some(vec![n[]]), Some(c[])), + PING(s, Some(t)) => Message::new(None, "PING", Some(vec![s[]]), Some(t[])), + PING(s, None) => Message::new(None, "PING", None, Some(s[])), + PONG(s, Some(t)) => Message::new(None, "PONG", Some(vec![s[]]), Some(t[])), + PONG(s, None) => Message::new(None, "PONG", None, Some(s[])), + ERROR(m) => Message::new(None, "ERROR", None, Some(m[])), + AWAY(Some(m)) => Message::new(None, "AWAY", None, Some(m[])), + AWAY(None) => Message::new(None, "AWAY", None, None), + REHASH => Message::new(None, "REHASH", None, None), + DIE => Message::new(None, "DIE", None, None), + RESTART => Message::new(None, "RESTART", None, None), + SUMMON(u, Some(t), Some(c)) => Message::new(None, "SUMMON", Some(vec![u[], t[]]), Some(c[])), + SUMMON(u, Some(t), None) => Message::new(None, "SUMMON", Some(vec![u[], t[]]), None), + SUMMON(u, None, _) => Message::new(None, "SUMMON", Some(vec![u[]]), None), + USERS(Some(t)) => Message::new(None, "USERS", None, Some(t[])), + USERS(None) => Message::new(None, "USERS", None, None), + WALLOPS(t) => Message::new(None, "WALLOPS", None, Some(t[])), + USERHOST(u) => Message::new(None, "USERHOST", Some(u.iter().map(|s| s[]).collect()), None), + ISON(u) => Message::new(None, "ISON", Some(u.iter().map(|s| s[]).collect()), None), + SAJOIN(n, c) => Message::new(None, "SAJOIN", Some(vec![n[], c[]]), None), + SAMODE(t, m, Some(p)) => Message::new(None, "SAMODE", Some(vec![t[], m[], p[]]), None), + SAMODE(t, m, None) => Message::new(None, "SAMODE", Some(vec![t[], m[]]), None), + SANICK(o, n) => Message::new(None, "SANICK", Some(vec![o[], n[]]), None), + SAPART(c, r) => Message::new(None, "SAPART", Some(vec![c[]]), Some(r[])), + SAQUIT(c, r) => Message::new(None, "SAQUIT", Some(vec![c[]]), Some(r[])), + } + } + + pub fn from_message(m: Message) -> IoResult { + Ok(if let "PASS" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 0 { return Err(invalid_input()) } + PASS(m.suffix.unwrap()) + } else { + if m.args.len() != 1 { return Err(invalid_input()) } + PASS(m.args[0].clone()) + } + } else if let "NICK" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 0 { return Err(invalid_input()) } + NICK(m.suffix.unwrap()) + } else { + if m.args.len() != 1 { return Err(invalid_input()) } + NICK(m.args[0].clone()) + } + } else if let "USER" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 2 { return Err(invalid_input()) } + USER(m.args[0].clone(), m.args[1].clone(), m.suffix.unwrap().clone()) + } else { + if m.args.len() != 3 { return Err(invalid_input()) } + USER(m.args[0].clone(), m.args[1].clone(), m.args[2].clone()) + } + } else if let "OPER" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 1 { return Err(invalid_input()) } + OPER(m.args[0].clone(), m.suffix.unwrap()) + } else { + if m.args.len() != 2 { return Err(invalid_input()) } + OPER(m.args[0].clone(), m.args[1].clone()) + } + } else if let "MODE" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 2 { return Err(invalid_input()) } + MODE(m.args[0].clone(), m.args[1].clone(), Some(m.suffix.unwrap().clone())) + } else if m.args.len() == 3 { + MODE(m.args[0].clone(), m.args[1].clone(), Some(m.args[2].clone())) + } else if m.args.len() == 2 { + MODE(m.args[0].clone(), m.args[1].clone(), None) + } else { + return Err(invalid_input()) + } + } else if let "SERVICE" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 5 { return Err(invalid_input()) } + SERVICE(m.args[0].clone(), m.args[1].clone(), m.args[2].clone(), m.args[3].clone(), + m.args[4].clone(), m.suffix.unwrap().clone()) + } else { + if m.args.len() != 6 { return Err(invalid_input()) } + SERVICE(m.args[0].clone(), m.args[1].clone(), m.args[2].clone(), m.args[3].clone(), + m.args[4].clone(), m.args[5].clone()) + } + } else if let "QUIT" = m.command[] { + if m.args.len() != 0 { return Err(invalid_input()) } + if m.suffix.is_some() { + QUIT(Some(m.suffix.unwrap().clone())) + } else { + QUIT(None) + } + } else if let "SQUIT" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 1 { return Err(invalid_input()) } + SQUIT(m.args[0].clone(), m.suffix.unwrap().clone()) + } else { + if m.args.len() != 2 { return Err(invalid_input()) } + SQUIT(m.args[0].clone(), m.args[1].clone()) + } + } else if let "JOIN" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + JOIN(m.suffix.unwrap().clone(), None) + } else if m.args.len() == 1 { + JOIN(m.args[0].clone(), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 1 { + JOIN(m.args[0].clone(), None) + } else if m.args.len() == 2 { + JOIN(m.args[0].clone(), Some(m.args[1].clone())) + } else { + return Err(invalid_input()) + } + } else if let "PART" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + PART(m.suffix.unwrap().clone(), None) + } else if m.args.len() == 1 { + PART(m.args[0].clone(), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 1 { + PART(m.args[0].clone(), None) + } else if m.args.len() == 2 { + PART(m.args[0].clone(), Some(m.args[1].clone())) + } else { + return Err(invalid_input()) + } + } else if let "TOPIC" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + TOPIC(m.suffix.unwrap().clone(), None) + } else if m.args.len() == 1 { + TOPIC(m.args[0].clone(), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 1 { + TOPIC(m.args[0].clone(), None) + } else if m.args.len() == 2 { + TOPIC(m.args[0].clone(), Some(m.args[1].clone())) + } else { + return Err(invalid_input()) + } + } else if let "NAMES" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + NAMES(Some(m.suffix.unwrap().clone()), None) + } else if m.args.len() == 1 { + NAMES(Some(m.args[0].clone()), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 0 { + NAMES(None, None) + } else if m.args.len() == 1 { + NAMES(Some(m.args[0].clone()), None) + } else if m.args.len() == 2 { + NAMES(Some(m.args[0].clone()), Some(m.args[1].clone())) + } else { + return Err(invalid_input()) + } + } else if let "LIST" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + LIST(Some(m.suffix.unwrap().clone()), None) + } else if m.args.len() == 1 { + LIST(Some(m.args[0].clone()), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 0 { + LIST(None, None) + } else if m.args.len() == 1 { + LIST(Some(m.args[0].clone()), None) + } else if m.args.len() == 2 { + LIST(Some(m.args[0].clone()), Some(m.args[1].clone())) + } else { + return Err(invalid_input()) + } + } else if let "INVITE" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 1 { return Err(invalid_input()) } + INVITE(m.args[0].clone(), m.suffix.unwrap()) + } else { + if m.args.len() != 2 { return Err(invalid_input()) } + INVITE(m.args[0].clone(), m.args[1].clone()) + } + } else if let "KICK" = m.command[] { + if m.args.len() != 2 { return Err(invalid_input()) } + KICK(m.args[0].clone(), m.args[1].clone(), m.suffix.clone()) + } else if let "PRIVMSG" = m.command[] { + if !m.suffix.is_some() || m.args.len() != 1 { return Err(invalid_input()) } + PRIVMSG(m.args[0].clone(), m.suffix.unwrap().clone()) + } else if let "NOTICE" = m.command[] { + if !m.suffix.is_some() || m.args.len() != 1 { return Err(invalid_input()) } + NOTICE(m.args[0].clone(), m.suffix.unwrap().clone()) + } else if let "MOTD" = m.command[] { + if m.args.len() != 0 { return Err(invalid_input()) } + if m.suffix.is_some() { + MOTD(Some(m.suffix.unwrap().clone())) + } else { + MOTD(None) + } + } else if let "LUSERS" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + LUSERS(Some(m.suffix.unwrap().clone()), None) + } else if m.args.len() == 1 { + LUSERS(Some(m.args[0].clone()), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 0 { + LUSERS(None, None) + } else if m.args.len() == 1 { + LUSERS(Some(m.args[0].clone()), None) + } else if m.args.len() == 2 { + LUSERS(Some(m.args[0].clone()), Some(m.args[1].clone())) + } else { + return Err(invalid_input()) + } + } else if let "VERSION" = m.command[] { + if m.args.len() != 0 { return Err(invalid_input()) } + if m.suffix.is_some() { + VERSION(Some(m.suffix.unwrap().clone())) + } else { + VERSION(None) + } + } else if let "STATS" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + STATS(Some(m.suffix.unwrap().clone()), None) + } else if m.args.len() == 1 { + STATS(Some(m.args[0].clone()), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 0 { + STATS(None, None) + } else if m.args.len() == 1 { + STATS(Some(m.args[0].clone()), None) + } else if m.args.len() == 2 { + STATS(Some(m.args[0].clone()), Some(m.args[1].clone())) + } else { + return Err(invalid_input()) + } + } else if let "LINKS" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + LINKS(None, Some(m.suffix.unwrap().clone())) + } else if m.args.len() == 1 { + LINKS(Some(m.args[0].clone()), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 0 { + LINKS(None, None) + } else { + return Err(invalid_input()) + } + } else if let "TIME" = m.command[] { + if m.args.len() != 0 { return Err(invalid_input()) } + if m.suffix.is_some() { + TIME(Some(m.suffix.unwrap().clone())) + } else { + TIME(None) + } + } else if let "CONNECT" = m.command[] { + if m.args.len() != 2 { return Err(invalid_input()) } + KICK(m.args[0].clone(), m.args[1].clone(), m.suffix.clone()) + } else if let "TRACE" = m.command[] { + if m.args.len() != 0 { return Err(invalid_input()) } + if m.suffix.is_some() { + TRACE(Some(m.suffix.unwrap().clone())) + } else { + TRACE(None) + } + } else if let "ADMIN" = m.command[] { + if m.args.len() != 0 { return Err(invalid_input()) } + if m.suffix.is_some() { + TIME(Some(m.suffix.unwrap().clone())) + } else { + TIME(None) + } + } else if let "INFO" = m.command[] { + if m.args.len() != 0 { return Err(invalid_input()) } + if m.suffix.is_some() { + TIME(Some(m.suffix.unwrap().clone())) + } else { + TIME(None) + } + } else if let "SERVLIST" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + SERVLIST(Some(m.suffix.unwrap().clone()), None) + } else if m.args.len() == 1 { + SERVLIST(Some(m.args[0].clone()), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 0 { + SERVLIST(None, None) + } else if m.args.len() == 1 { + SERVLIST(Some(m.args[0].clone()), None) + } else if m.args.len() == 2 { + SERVLIST(Some(m.args[0].clone()), Some(m.args[1].clone())) + } else { + return Err(invalid_input()) + } + } else if let "SQUERY" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 1 { return Err(invalid_input()) } + SQUERY(m.args[0].clone(), m.suffix.unwrap()) + } else { + if m.args.len() != 2 { return Err(invalid_input()) } + SQUERY(m.args[0].clone(), m.args[1].clone()) + } + } else if let "WHO" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + WHO(Some(m.suffix.unwrap().clone()), None) + } else if m.args.len() == 1 { + WHO(Some(m.args[0].clone()), Some(m.suffix.unwrap()[] == "o")) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 0 { + WHO(None, None) + } else if m.args.len() == 1 { + WHO(Some(m.args[0].clone()), None) + } else if m.args.len() == 2 { + WHO(Some(m.args[0].clone()), Some(m.args[1][] == "o")) + } else { + return Err(invalid_input()) + } + } else if let "WHOIS" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + WHOIS(None, m.suffix.unwrap().clone()) + } else if m.args.len() == 1 { + WHOIS(Some(m.args[0].clone()), m.suffix.unwrap().clone()) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 1 { + WHOIS(None, m.args[0].clone()) + } else if m.args.len() == 2 { + WHOIS(Some(m.args[0].clone()), m.args[1].clone()) + } else { + return Err(invalid_input()) + } + } else if let "WHOWAS" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + WHOWAS(m.suffix.unwrap().clone(), None, None) + } else if m.args.len() == 1 { + WHOWAS(m.args[0].clone(), None, Some(m.suffix.unwrap().clone())) + } else if m.args.len() == 2 { + WHOWAS(m.args[0].clone(), Some(m.args[1].clone()), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 1 { + WHOWAS(m.args[0].clone(), None, None) + } else if m.args.len() == 2 { + WHOWAS(m.args[0].clone(), None, Some(m.args[1].clone())) + } else if m.args.len() == 3 { + WHOWAS(m.args[0].clone(), Some(m.args[1].clone()), Some(m.args[2].clone())) + } else { + return Err(invalid_input()) + } + } else if let "KILL" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 1 { return Err(invalid_input()) } + KILL(m.args[0].clone(), m.suffix.unwrap()) + } else { + if m.args.len() != 2 { return Err(invalid_input()) } + KILL(m.args[0].clone(), m.args[1].clone()) + } + } else if let "PING" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + PING(m.suffix.unwrap().clone(), None) + } else if m.args.len() == 1 { + PING(m.args[0].clone(), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 1 { + PING(m.args[0].clone(), None) + } else if m.args.len() == 2 { + PING(m.args[0].clone(), Some(m.args[1].clone())) + } else { + return Err(invalid_input()) + } + } else if let "PONG" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + PONG(m.suffix.unwrap().clone(), None) + } else if m.args.len() == 1 { + PONG(m.args[0].clone(), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 1 { + PONG(m.args[0].clone(), None) + } else if m.args.len() == 2 { + PONG(m.args[0].clone(), Some(m.args[1].clone())) + } else { + return Err(invalid_input()) + } + } else if let "ERROR" = m.command[] { + if m.suffix.is_some() && m.args.len() == 0 { + ERROR(m.suffix.unwrap().clone()) + } else { + return Err(invalid_input()) + } + } else if let "AWAY" = m.command[] { + if m.args.len() == 0 { + AWAY(m.suffix.clone()) + } else { + return Err(invalid_input()) + } + } else if let "REHASH" = m.command[] { + if m.args.len() == 0 { + REHASH + } else { + return Err(invalid_input()) + } + } else if let "DIE" = m.command[] { + if m.args.len() == 0 { + DIE + } else { + return Err(invalid_input()) + } + } else if let "RESTART" = m.command[] { + if m.args.len() == 0 { + RESTART + } else { + return Err(invalid_input()) + } + } else if let "SUMMON" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 0 { + SUMMON(m.suffix.unwrap().clone(), None, None) + } else if m.args.len() == 1 { + SUMMON(m.args[0].clone(), Some(m.suffix.unwrap().clone()), None) + } else if m.args.len() == 2 { + SUMMON(m.args[0].clone(), Some(m.args[1].clone()), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 1 { + SUMMON(m.args[0].clone(), None, None) + } else if m.args.len() == 2 { + SUMMON(m.args[0].clone(), Some(m.args[1].clone()), None) + } else if m.args.len() == 3 { + SUMMON(m.args[0].clone(), Some(m.args[1].clone()), Some(m.args[2].clone())) + } else { + return Err(invalid_input()) + } + } else if let "USERS" = m.command[] { + if m.args.len() == 0 { + USERS(m.suffix.clone()) + } else if m.args.len() == 1 { + USERS(Some(m.args[0].clone())) + } else { + return Err(invalid_input()) + } + } else if let "WALLOPS" = m.command[] { + if m.suffix.is_some() && m.args.len() == 0 { + WALLOPS(m.suffix.unwrap().clone()) + } else if m.args.len() == 1 { + WALLOPS(m.args[0].clone()) + } else { + return Err(invalid_input()) + } + } else if let "USERHOST" = m.command[] { + if m.suffix.is_none() { + USERHOST(m.args.clone()) + } else { + return Err(invalid_input()) + } + } else if let "ISON" = m.command[] { + if m.suffix.is_none() { + USERHOST(m.args.clone()) + } else { + return Err(invalid_input()) + } + } else if let "SAJOIN" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 1 { return Err(invalid_input()) } + SAJOIN(m.args[0].clone(), m.suffix.unwrap()) + } else { + if m.args.len() != 2 { return Err(invalid_input()) } + SAJOIN(m.args[0].clone(), m.args[1].clone()) + } + } else if let "SAMODE" = m.command[] { + if m.suffix.is_some() { + if m.args.len() == 1 { + SAMODE(m.args[0].clone(), m.suffix.unwrap().clone(), None) + } else if m.args.len() == 2 { + SAMODE(m.args[0].clone(), m.args[1].clone(), Some(m.suffix.unwrap().clone())) + } else { + return Err(invalid_input()) + } + } else if m.args.len() == 2 { + SAMODE(m.args[0].clone(), m.args[1].clone(), None) + } else if m.args.len() == 3 { + SAMODE(m.args[0].clone(), m.args[1].clone(), Some(m.args[2].clone())) + } else { + return Err(invalid_input()) + } + } else if let "SANICK" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 1 { return Err(invalid_input()) } + SANICK(m.args[0].clone(), m.suffix.unwrap()) + } else { + if m.args.len() != 2 { return Err(invalid_input()) } + SANICK(m.args[0].clone(), m.args[1].clone()) + } + } else if let "SAPART" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 1 { return Err(invalid_input()) } + SAPART(m.args[0].clone(), m.suffix.unwrap()) + } else { + if m.args.len() != 2 { return Err(invalid_input()) } + SAPART(m.args[0].clone(), m.args[1].clone()) + } + } else if let "SAQUIT" = m.command[] { + if m.suffix.is_some() { + if m.args.len() != 1 { return Err(invalid_input()) } + SAQUIT(m.args[0].clone(), m.suffix.unwrap()) + } else { + if m.args.len() != 2 { return Err(invalid_input()) } + SAQUIT(m.args[0].clone(), m.args[1].clone()) + } + } else { + return Err(invalid_input()) + }) + } +} + +fn invalid_input() -> IoError { + IoError { + kind: InvalidInput, + desc: "Failed to parse malformed message as command.", + detail: None + } +} diff --git a/src/data/mod.rs b/src/data/mod.rs index 471a5ed..4d4343f 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -5,5 +5,6 @@ pub mod kinds { impl IrcReader for T where T: Buffer + Sized + Send + 'static {} } +pub mod command; pub mod config; pub mod message; diff --git a/src/server.rs b/src/server.rs index 425c942..0120c7c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,11 +1,12 @@ use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; use conn::Connection; +use data::command::Command; use data::config::Config; use data::kinds::{IrcReader, IrcWriter}; use data::message::Message; pub trait Server<'a, T, U> { - fn send(&self, _: Message) -> IoResult<()>; + fn send(&self, _: Command) -> IoResult<()>; fn iter(&'a self) -> ServerIterator<'a, T, U>; } @@ -26,8 +27,8 @@ impl<'a> IrcServer<'a, BufferedWriter, BufferedReader> { } impl<'a, T, U> Server<'a, T, U> for IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader{ - fn send(&self, message: Message) -> IoResult<()> { - self.conn.send(message) + fn send(&self, command: Command) -> IoResult<()> { + self.conn.send(command.to_message()) } fn iter(&'a self) -> ServerIterator<'a, T, U> { From 5bbde7e96c52840d3f2a8b7b20d3cb67e658bcc3 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Mon, 3 Nov 2014 00:52:15 -0500 Subject: [PATCH 08/18] Added documentation everywhere. --- src/conn.rs | 12 +++++ src/data/command.rs | 107 ++++++++++++++++++++++++-------------------- src/data/config.rs | 19 ++++++++ src/data/message.rs | 13 ++++++ src/data/mod.rs | 10 +++++ src/lib.rs | 1 + src/server.rs | 32 +++++++++++-- 7 files changed, 141 insertions(+), 53 deletions(-) diff --git a/src/conn.rs b/src/conn.rs index 9961f7a..b057c50 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -1,14 +1,20 @@ +//! Thread-safe connections on any IrcWriters and IrcReaders +#![experimental] use std::sync::Mutex; use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; use data::kinds::{IrcWriter, IrcReader}; use data::message::Message; +/// A thread-safe connection +#[experimental] pub struct Connection where T: IrcWriter, U: IrcReader { writer: Mutex, reader: Mutex, } impl Connection, BufferedReader> { + /// Creates a thread-safe TCP connection to the specified server + #[experimental] pub fn connect(host: &str, port: u16) -> IoResult, BufferedReader>> { let socket = try!(TcpStream::connect(host, port)); Ok(Connection::new(BufferedWriter::new(socket.clone()), BufferedReader::new(socket))) @@ -16,6 +22,8 @@ impl Connection, BufferedReader> { } impl Connection where T: IrcWriter, U: IrcReader { + /// Creates a new connection from any arbitrary IrcWriter and IrcReader + #[experimental] pub fn new(writer: T, reader: U) -> Connection { Connection { writer: Mutex::new(writer), @@ -23,12 +31,16 @@ impl Connection where T: IrcWriter, U: IrcReader { } } + /// Sends a Message over this connection + #[experimental] pub fn send(&self, message: Message) -> IoResult<()> { let mut send = self.writer.lock(); try!(send.write_str(message.into_string()[])); send.flush() } + /// Receives a single line from this connection + #[experimental] pub fn recv(&self) -> IoResult { self.reader.lock().read_line() } diff --git a/src/data/command.rs b/src/data/command.rs index 2c15378..1bcaa07 100644 --- a/src/data/command.rs +++ b/src/data/command.rs @@ -1,101 +1,104 @@ +//! Enumeration of all available client commands +#![stable] use std::io::{InvalidInput, IoError, IoResult}; use data::message::Message; -/// List of all client commands as defined in RFC 2812. +/// List of all client commands as defined in [RFC 2812](http://tools.ietf.org/html/rfc2812) +#[stable] #[deriving(Show, PartialEq)] pub enum Command { // 3.1 Connection Registration - /// PASS + /// PASS password PASS(String), - /// NICK + /// NICK nickname NICK(String), - /// USER * + /// USER user mode * realname USER(String, String, String), - /// OPER + /// OPER name password OPER(String, String), - /// MODE - /// MODE [] + /// MODE nickname modes + /// MODE channel modes [modeparams] MODE(String, String, Option), - /// SERVICE + /// SERVICE nickname reserved distribution type reserved info SERVICE(String, String, String, String, String, String), - /// QUIT + /// QUIT Quit Message QUIT(Option), - /// SQUIT + /// SQUIT server comment SQUIT(String, String), // 3.2 Channel operations - /// JOIN [] + /// JOIN chanlist [chankeys] JOIN(String, Option), - /// PART [] + /// PART chanlist [Part Message] PART(String, Option), // MODE is already defined. // MODE(String, String, Option), - /// TOPIC [] + /// TOPIC channel [topic] TOPIC(String, Option), - /// NAMES [ []] + /// NAMES [chanlist [target]] NAMES(Option, Option), - /// LIST [ []] + /// LIST [chanlist [target]] LIST(Option, Option), - /// INVITE + /// INVITE nickname channel INVITE(String, String), - /// KICK [] + /// KICK chanlist userlist [comment] KICK(String, String, Option), // 3.3 Sending messages - /// PRIVMSG + /// PRIVMSG msgtarget text to be sent PRIVMSG(String, String), - /// NOTICE + /// NOTICE msgtarget text NOTICE(String, String), // 3.4 Server queries and commands - /// MOTD [] + /// MOTD [target] MOTD(Option), - /// LUSERS [ []] + /// LUSERS [mask [target]] LUSERS(Option, Option), - /// VERSION [] + /// VERSION [target] VERSION(Option), - /// STATS [ []] + /// STATS [query [target]] STATS(Option, Option), - /// LINKS [[] ] + /// LINKS [[remote server] server mask] LINKS(Option, Option), - /// TIME [] + /// TIME [target] TIME(Option), - /// CONNECT [] + /// CONNECT target server port [remote server] CONNECT(String, String, Option), - /// TRACE [] + /// TRACE [target] TRACE(Option), - /// ADMIN [] + /// ADMIN [target] ADMIN(Option), - /// INFO [] + /// INFO [target] INFO(Option), // 3.5 Service Query and Commands - /// SERVLIST [ []] + /// SERVLIST [mask [type]] SERVLIST(Option, Option), - /// SQUERY + /// SQUERY servicename text SQUERY(String, String), // 3.6 User based queries - /// WHO [ ["o"]] + /// WHO [mask ["o"]] WHO(Option, Option), - /// WHOIS [] + /// WHOIS [target] masklist WHOIS(Option, String), - /// WHOWAS [ []] + /// WHOWAS nicklist [count [target]] WHOWAS(String, Option, Option), // 3.7 Miscellaneous messages - /// KILL + /// KILL nickname comment KILL(String, String), - /// PING [] + /// PING server1 [server2] PING(String, Option), - /// PONG [] + /// PONG server [server2] PONG(String, Option), - /// ERROR + /// ERROR error message ERROR(String), // 4 Optional Features - /// AWAY [] + /// AWAY [text] AWAY(Option), /// REHASH REHASH, @@ -103,31 +106,33 @@ pub enum Command { DIE, /// RESTART RESTART, - /// SUMMON [ []] + /// SUMMON user [target [channel]] SUMMON(String, Option, Option), - /// USERS [] + /// USERS [target] USERS(Option), - /// WALLOPS + /// WALLOPS Text to be sent WALLOPS(String), - /// USERHOST + /// USERHOST space-separated nicklist USERHOST(Vec), - /// ISON + /// ISON space-separated nicklist ISON(Vec), // Non-RFC commands from InspIRCd - /// SAJOIN + /// SAJOIN nickname channel SAJOIN(String, String), - /// SAMODE [] + /// SAMODE target modes [modeparams] SAMODE(String, String, Option), - /// SANICK + /// SANICK old nickname new nickname SANICK(String, String), - /// SAPART + /// SAPART nickname reason SAPART(String, String), - /// SAQUIT + /// SAQUIT nickname reason SAQUIT(String, String), } impl Command { + /// Converts a Command into a Message + #[stable] pub fn to_message(self) -> Message { match self { PASS(p) => Message::new(None, "PASS", None, Some(p[])), @@ -221,6 +226,8 @@ impl Command { } } + /// Converts a Message into a Command + #[stable] pub fn from_message(m: Message) -> IoResult { Ok(if let "PASS" = m.command[] { if m.suffix.is_some() { @@ -738,6 +745,8 @@ impl Command { } } +/// Produces an invalid_input IoError +#[stable] fn invalid_input() -> IoError { IoError { kind: InvalidInput, diff --git a/src/data/config.rs b/src/data/config.rs index 5fd3d0d..3b65f26 100644 --- a/src/data/config.rs +++ b/src/data/config.rs @@ -1,22 +1,37 @@ +//! JSON configuration files using libserialize +#![stable] use std::collections::HashMap; use std::io::fs::File; use std::io::{InvalidInput, IoError, IoResult}; use serialize::json::decode; +/// Configuration data #[deriving(Clone, Decodable)] +#[unstable] pub struct Config { + /// A list of the owners of the bot by nickname pub owners: Vec, + /// The bot's nickname pub nickname: String, + /// The bot's username pub username: String, + /// The bot's real name pub realname: String, + /// The bot's password pub password: String, + /// The server to connect to pub server: String, + /// The port to connect on pub port: u16, + /// A list of channels to join on connection pub channels: Vec, + /// A map of additional options to be stored in config pub options: HashMap, } impl Config { + /// Loads a JSON configuration from the desired path. + #[stable] pub fn load(path: Path) -> IoResult { let mut file = try!(File::open(&path)); let data = try!(file.read_to_string()); @@ -27,10 +42,14 @@ impl Config { }) } + /// Loads a JSON configuration using the string as a UTF-8 path. + #[stable] pub fn load_utf8(path: &str) -> IoResult { Config::load(Path::new(path)) } + /// Determines whether or not the nickname provided is the owner of the bot. + #[stable] pub fn is_owner(&self, nickname: &str) -> bool { self.owners[].contains(&String::from_str(nickname)) } diff --git a/src/data/message.rs b/src/data/message.rs index 9665cd5..dac1b8a 100644 --- a/src/data/message.rs +++ b/src/data/message.rs @@ -1,14 +1,25 @@ +//! Messages to and from the server +#![experimental] use std::from_str::FromStr; +/// IRC Message data +#[experimental] #[deriving(Clone, PartialEq, Show)] pub struct Message { + /// The message prefix (or source) as defined by [RFC 2812](http://tools.ietf.org/html/rfc2812) pub prefix: Option, + /// The IRC command as defined by [RFC 2812](http://tools.ietf.org/html/rfc2812) pub command: String, + /// The command arguments pub args: Vec, + /// The message suffix as defined by [RFC 2812](http://tools.ietf.org/html/rfc2812) + /// This is the only part of the message that is allowed to contain spaces. pub suffix: Option, } impl Message { + /// Creates a new Message + #[experimental] pub fn new(prefix: Option<&str>, command: &str, args: Option>, suffix: Option<&str>) -> Message { Message { @@ -19,6 +30,8 @@ impl Message { } } + /// Converts a Message into a String according to the IRC protocol + #[experimental] pub fn into_string(&self) -> String { let mut ret = String::new(); if let Some(ref prefix) = self.prefix { diff --git a/src/data/mod.rs b/src/data/mod.rs index 4d4343f..474ba64 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -1,6 +1,16 @@ +//! Data related to IRC functionality +#![experimental] + pub mod kinds { + //! Trait definitions of appropriate Writers and Buffers for use with this library + #![unstable] + + /// Trait describing all possible Writers for this library + #[unstable] pub trait IrcWriter: Writer + Sized + Send + 'static {} impl IrcWriter for T where T: Writer + Sized + Send + 'static {} + /// Trait describing all possible Readers for this library + #[unstable] pub trait IrcReader: Buffer + Sized + Send + 'static {} impl IrcReader for T where T: Buffer + Sized + Send + 'static {} } diff --git a/src/lib.rs b/src/lib.rs index 2de2166..8c902f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ //! A simple, thread-safe IRC client library. #![crate_name = "irc"] #![crate_type = "lib"] +#![unstable] #![feature(if_let)] #![feature(slicing_syntax)] diff --git a/src/server.rs b/src/server.rs index 0120c7c..06b735f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,3 +1,5 @@ +//! Interface for working with IRC Servers +#![experimental] use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; use conn::Connection; use data::command::Command; @@ -5,18 +7,30 @@ use data::config::Config; use data::kinds::{IrcReader, IrcWriter}; use data::message::Message; +/// Trait describing core Server functionality +#[experimental] pub trait Server<'a, T, U> { + /// Gets the configuration being used with this Server + fn config(&self) -> &Config; + /// Sends a Command to this Server fn send(&self, _: Command) -> IoResult<()>; + /// Gets an Iterator over Messages received by this Server fn iter(&'a self) -> ServerIterator<'a, T, U>; } +/// A thread-safe implementation of an IRC Server connection +#[experimental] pub struct IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { - pub conn: Connection, - pub config: Config + /// The thread-safe IRC connection + conn: Connection, + /// The configuration used with this connection + config: Config } impl<'a> IrcServer<'a, BufferedWriter, BufferedReader> { - pub fn new(config: &str) -> IoResult, BufferedReader>> { + /// Creates a new IRC Server connection from the specified configuration, connecting immediately. + #[experimental] + pub fn new(config: &str) -> IoResult, BufferedReader>> { let config = try!(Config::load_utf8(config)); let conn = try!(Connection::connect(config.server[], config.port)); Ok(IrcServer { @@ -26,7 +40,11 @@ impl<'a> IrcServer<'a, BufferedWriter, BufferedReader> { } } -impl<'a, T, U> Server<'a, T, U> for IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader{ +impl<'a, T, U> Server<'a, T, U> for IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { + fn config(&self) -> &Config { + &self.config + } + fn send(&self, command: Command) -> IoResult<()> { self.conn.send(command.to_message()) } @@ -37,6 +55,8 @@ impl<'a, T, U> Server<'a, T, U> for IrcServer<'a, T, U> where T: IrcWriter, U: I } impl<'a, T, U> IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { + /// Creates an IRC server from the specified configuration, and any arbitrary Connection + #[experimental] pub fn from_connection(config: &str, conn: Connection) -> IoResult> { Ok(IrcServer { conn: conn, @@ -45,11 +65,15 @@ impl<'a, T, U> IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { } } +/// An Iterator over an IrcServer's incoming Messages +#[experimental] pub struct ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader { pub server: &'a IrcServer<'a, T, U> } impl<'a, T, U> ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader { + /// Creates a new ServerIterator for the desired IrcServer + #[experimental] pub fn new(server: &'a IrcServer<'a, T, U>) -> ServerIterator<'a, T, U> { ServerIterator { server: server From fbcc3021a8db8ffff39599b78fca65fdee00e271 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Mon, 3 Nov 2014 00:55:56 -0500 Subject: [PATCH 09/18] Added server utils module to hold shortcuts and the like. --- src/{server.rs => server/mod.rs} | 2 ++ src/server/utils.rs | 2 ++ 2 files changed, 4 insertions(+) rename src/{server.rs => server/mod.rs} (99%) create mode 100644 src/server/utils.rs diff --git a/src/server.rs b/src/server/mod.rs similarity index 99% rename from src/server.rs rename to src/server/mod.rs index 06b735f..3ed1321 100644 --- a/src/server.rs +++ b/src/server/mod.rs @@ -7,6 +7,8 @@ use data::config::Config; use data::kinds::{IrcReader, IrcWriter}; use data::message::Message; +pub mod utils; + /// Trait describing core Server functionality #[experimental] pub trait Server<'a, T, U> { diff --git a/src/server/utils.rs b/src/server/utils.rs new file mode 100644 index 0000000..07cdcfd --- /dev/null +++ b/src/server/utils.rs @@ -0,0 +1,2 @@ +//! Utilities and shortcuts for working with IRC servers +#![experimental] From 25d4585fbca40c1589b09cf132ed615d66eb2991 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Mon, 3 Nov 2014 01:45:00 -0500 Subject: [PATCH 10/18] Commands now uses slices. --- src/data/command.rs | 468 ++++++++++++++++++++++---------------------- 1 file changed, 233 insertions(+), 235 deletions(-) diff --git a/src/data/command.rs b/src/data/command.rs index 1bcaa07..1fee292 100644 --- a/src/data/command.rs +++ b/src/data/command.rs @@ -6,100 +6,100 @@ use data::message::Message; /// List of all client commands as defined in [RFC 2812](http://tools.ietf.org/html/rfc2812) #[stable] #[deriving(Show, PartialEq)] -pub enum Command { +pub enum Command<'a> { // 3.1 Connection Registration /// PASS password - PASS(String), + PASS(&'a str), /// NICK nickname - NICK(String), + NICK(&'a str), /// USER user mode * realname - USER(String, String, String), + USER(&'a str, &'a str, &'a str), /// OPER name password - OPER(String, String), + OPER(&'a str, &'a str), /// MODE nickname modes /// MODE channel modes [modeparams] - MODE(String, String, Option), + MODE(&'a str, &'a str, Option<&'a str>), /// SERVICE nickname reserved distribution type reserved info - SERVICE(String, String, String, String, String, String), + SERVICE(&'a str, &'a str, &'a str, &'a str, &'a str, &'a str), /// QUIT Quit Message - QUIT(Option), + QUIT(Option<&'a str>), /// SQUIT server comment - SQUIT(String, String), + SQUIT(&'a str, &'a str), // 3.2 Channel operations /// JOIN chanlist [chankeys] - JOIN(String, Option), + JOIN(&'a str, Option<&'a str>), /// PART chanlist [Part Message] - PART(String, Option), + PART(&'a str, Option<&'a str>), // MODE is already defined. - // MODE(String, String, Option), + // MODE(&'a str, &'a str, Option<&'a str>), /// TOPIC channel [topic] - TOPIC(String, Option), + TOPIC(&'a str, Option<&'a str>), /// NAMES [chanlist [target]] - NAMES(Option, Option), + NAMES(Option<&'a str>, Option<&'a str>), /// LIST [chanlist [target]] - LIST(Option, Option), + LIST(Option<&'a str>, Option<&'a str>), /// INVITE nickname channel - INVITE(String, String), + INVITE(&'a str, &'a str), /// KICK chanlist userlist [comment] - KICK(String, String, Option), + KICK(&'a str, &'a str, Option<&'a str>), // 3.3 Sending messages /// PRIVMSG msgtarget text to be sent - PRIVMSG(String, String), + PRIVMSG(&'a str, &'a str), /// NOTICE msgtarget text - NOTICE(String, String), + NOTICE(&'a str, &'a str), // 3.4 Server queries and commands /// MOTD [target] - MOTD(Option), + MOTD(Option<&'a str>), /// LUSERS [mask [target]] - LUSERS(Option, Option), + LUSERS(Option<&'a str>, Option<&'a str>), /// VERSION [target] - VERSION(Option), + VERSION(Option<&'a str>), /// STATS [query [target]] - STATS(Option, Option), + STATS(Option<&'a str>, Option<&'a str>), /// LINKS [[remote server] server mask] - LINKS(Option, Option), + LINKS(Option<&'a str>, Option<&'a str>), /// TIME [target] - TIME(Option), + TIME(Option<&'a str>), /// CONNECT target server port [remote server] - CONNECT(String, String, Option), + CONNECT(&'a str, &'a str, Option<&'a str>), /// TRACE [target] - TRACE(Option), + TRACE(Option<&'a str>), /// ADMIN [target] - ADMIN(Option), + ADMIN(Option<&'a str>), /// INFO [target] - INFO(Option), + INFO(Option<&'a str>), // 3.5 Service Query and Commands /// SERVLIST [mask [type]] - SERVLIST(Option, Option), + SERVLIST(Option<&'a str>, Option<&'a str>), /// SQUERY servicename text - SQUERY(String, String), + SQUERY(&'a str, &'a str), // 3.6 User based queries /// WHO [mask ["o"]] - WHO(Option, Option), + WHO(Option<&'a str>, Option), /// WHOIS [target] masklist - WHOIS(Option, String), + WHOIS(Option<&'a str>, &'a str), /// WHOWAS nicklist [count [target]] - WHOWAS(String, Option, Option), + WHOWAS(&'a str, Option<&'a str>, Option<&'a str>), // 3.7 Miscellaneous messages /// KILL nickname comment - KILL(String, String), + KILL(&'a str, &'a str), /// PING server1 [server2] - PING(String, Option), + PING(&'a str, Option<&'a str>), /// PONG server [server2] - PONG(String, Option), + PONG(&'a str, Option<&'a str>), /// ERROR error message - ERROR(String), + ERROR(&'a str), // 4 Optional Features /// AWAY [text] - AWAY(Option), + AWAY(Option<&'a str>), /// REHASH REHASH, /// DIE @@ -107,353 +107,351 @@ pub enum Command { /// RESTART RESTART, /// SUMMON user [target [channel]] - SUMMON(String, Option, Option), + SUMMON(&'a str, Option<&'a str>, Option<&'a str>), /// USERS [target] - USERS(Option), + USERS(Option<&'a str>), /// WALLOPS Text to be sent - WALLOPS(String), + WALLOPS(&'a str), /// USERHOST space-separated nicklist - USERHOST(Vec), + USERHOST(Vec<&'a str>), /// ISON space-separated nicklist - ISON(Vec), + ISON(Vec<&'a str>), // Non-RFC commands from InspIRCd /// SAJOIN nickname channel - SAJOIN(String, String), + SAJOIN(&'a str, &'a str), /// SAMODE target modes [modeparams] - SAMODE(String, String, Option), + SAMODE(&'a str, &'a str, Option<&'a str>), /// SANICK old nickname new nickname - SANICK(String, String), + SANICK(&'a str, &'a str), /// SAPART nickname reason - SAPART(String, String), + SAPART(&'a str, &'a str), /// SAQUIT nickname reason - SAQUIT(String, String), + SAQUIT(&'a str, &'a str), } -impl Command { +impl<'a> Command<'a> { /// Converts a Command into a Message #[stable] pub fn to_message(self) -> Message { match self { - PASS(p) => Message::new(None, "PASS", None, Some(p[])), - NICK(n) => Message::new(None, "NICK", None, Some(n[])), - USER(u, m, r) => Message::new(None, "USER", Some(vec![u[], m[], "*"]), Some(r[])), - OPER(u, p) => Message::new(None, "OPER", Some(vec![u[]]), Some(p[])), - MODE(t, m, Some(p)) => Message::new(None, "MODE", Some(vec![t[], m[], p[]]), None), - MODE(t, m, None) => Message::new(None, "MODE", Some(vec![t[], m[]]), None), + PASS(p) => Message::new(None, "PASS", None, Some(p)), + NICK(n) => Message::new(None, "NICK", None, Some(n)), + USER(u, m, r) => Message::new(None, "USER", Some(vec![u, m, "*"]), Some(r)), + OPER(u, p) => Message::new(None, "OPER", Some(vec![u]), Some(p)), + MODE(t, m, Some(p)) => Message::new(None, "MODE", Some(vec![t, m, p]), None), + MODE(t, m, None) => Message::new(None, "MODE", Some(vec![t, m]), None), SERVICE(n, r, d, t, re, i) => Message::new(None, "SERVICE", - Some(vec![n[], r[], d[], t[], re[]]), Some(i[])), - QUIT(Some(m)) => Message::new(None, "QUIT", None, Some(m[])), + Some(vec![n, r, d, t, re]), Some(i)), + QUIT(Some(m)) => Message::new(None, "QUIT", None, Some(m)), QUIT(None) => Message::new(None, "QUIT", None, None), - SQUIT(s, c) => Message::new(None, "SQUIT", Some(vec![s[]]), Some(c[])), - JOIN(c, Some(k)) => Message::new(None, "JOIN", Some(vec![c[], k[]]), None), - JOIN(c, None) => Message::new(None, "JOIN", Some(vec![c[]]), None), - PART(c, Some(m)) => Message::new(None, "PART", Some(vec![c[]]), Some(m[])), - PART(c, None) => Message::new(None, "PART", Some(vec![c[]]), None), - TOPIC(c, Some(t)) => Message::new(None, "TOPIC", Some(vec![c[]]), Some(t[])), - TOPIC(c, None) => Message::new(None, "TOPIC", Some(vec![c[]]), None), - NAMES(Some(c), Some(t)) => Message::new(None, "NAMES", Some(vec![c[]]), Some(t[])), - NAMES(Some(c), None) => Message::new(None, "NAMES", Some(vec![c[]]), None), + SQUIT(s, c) => Message::new(None, "SQUIT", Some(vec![s]), Some(c)), + JOIN(c, Some(k)) => Message::new(None, "JOIN", Some(vec![c, k]), None), + JOIN(c, None) => Message::new(None, "JOIN", Some(vec![c]), None), + PART(c, Some(m)) => Message::new(None, "PART", Some(vec![c]), Some(m)), + PART(c, None) => Message::new(None, "PART", Some(vec![c]), None), + TOPIC(c, Some(t)) => Message::new(None, "TOPIC", Some(vec![c]), Some(t)), + TOPIC(c, None) => Message::new(None, "TOPIC", Some(vec![c]), None), + NAMES(Some(c), Some(t)) => Message::new(None, "NAMES", Some(vec![c]), Some(t)), + NAMES(Some(c), None) => Message::new(None, "NAMES", Some(vec![c]), None), NAMES(None, _) => Message::new(None, "NAMES", None, None), - LIST(Some(c), Some(t)) => Message::new(None, "LIST", Some(vec![c[]]), Some(t[])), - LIST(Some(c), None) => Message::new(None, "LIST", Some(vec![c[]]), None), + LIST(Some(c), Some(t)) => Message::new(None, "LIST", Some(vec![c]), Some(t)), + LIST(Some(c), None) => Message::new(None, "LIST", Some(vec![c]), None), LIST(None, _) => Message::new(None, "LIST", None, None), - INVITE(n, c) => Message::new(None, "INVITE", Some(vec![n[], c[]]), None), - KICK(c, n, Some(r)) => Message::new(None, "KICK", Some(vec![c[], n[]]), Some(r[])), - KICK(c, n, None) => Message::new(None, "KICK", Some(vec![c[], n[]]), None), - PRIVMSG(t, m) => Message::new(None, "PRIVMSG", Some(vec![t[]]), Some(m[])), - NOTICE(t, m) => Message::new(None, "NOTICE", Some(vec![t[]]), Some(m[])), - MOTD(Some(t)) => Message::new(None, "MOTD", None, Some(t[])), + INVITE(n, c) => Message::new(None, "INVITE", Some(vec![n, c]), None), + KICK(c, n, Some(r)) => Message::new(None, "KICK", Some(vec![c, n]), Some(r)), + KICK(c, n, None) => Message::new(None, "KICK", Some(vec![c, n]), None), + PRIVMSG(t, m) => Message::new(None, "PRIVMSG", Some(vec![t]), Some(m)), + NOTICE(t, m) => Message::new(None, "NOTICE", Some(vec![t]), Some(m)), + MOTD(Some(t)) => Message::new(None, "MOTD", None, Some(t)), MOTD(None) => Message::new(None, "MOTD", None, None), - LUSERS(Some(m), Some(t)) => Message::new(None, "LUSERS", Some(vec![m[]]), Some(t[])), - LUSERS(Some(m), None) => Message::new(None, "LUSERS", Some(vec![m[]]), None), + LUSERS(Some(m), Some(t)) => Message::new(None, "LUSERS", Some(vec![m]), Some(t)), + LUSERS(Some(m), None) => Message::new(None, "LUSERS", Some(vec![m]), None), LUSERS(None, _) => Message::new(None, "LUSERS", None, None), - VERSION(Some(t)) => Message::new(None, "VERSION", None, Some(t[])), + VERSION(Some(t)) => Message::new(None, "VERSION", None, Some(t)), VERSION(None) => Message::new(None, "VERSION", None, None), - STATS(Some(q), Some(t)) => Message::new(None, "STATS", Some(vec![q[]]), Some(t[])), - STATS(Some(q), None) => Message::new(None, "STATS", Some(vec![q[]]), None), + STATS(Some(q), Some(t)) => Message::new(None, "STATS", Some(vec![q]), Some(t)), + STATS(Some(q), None) => Message::new(None, "STATS", Some(vec![q]), None), STATS(None, _) => Message::new(None, "STATS", None, None), - LINKS(Some(r), Some(s)) => Message::new(None, "LINKS", Some(vec![r[]]), Some(s[])), - LINKS(None, Some(s)) => Message::new(None, "LINKS", None, Some(s[])), + LINKS(Some(r), Some(s)) => Message::new(None, "LINKS", Some(vec![r]), Some(s)), + LINKS(None, Some(s)) => Message::new(None, "LINKS", None, Some(s)), LINKS(_, None) => Message::new(None, "LINKS", None, None), - TIME(Some(t)) => Message::new(None, "TIME", None, Some(t[])), + TIME(Some(t)) => Message::new(None, "TIME", None, Some(t)), TIME(None) => Message::new(None, "TIME", None, None), - CONNECT(t, p, Some(r)) => Message::new(None, "CONNECT", Some(vec![t[], p[]]), Some(r[])), - CONNECT(t, p, None) => Message::new(None, "CONNECT", Some(vec![t[], p[]]), None), - TRACE(Some(t)) => Message::new(None, "TRACE", None, Some(t[])), + CONNECT(t, p, Some(r)) => Message::new(None, "CONNECT", Some(vec![t, p]), Some(r)), + CONNECT(t, p, None) => Message::new(None, "CONNECT", Some(vec![t, p]), None), + TRACE(Some(t)) => Message::new(None, "TRACE", None, Some(t)), TRACE(None) => Message::new(None, "TRACE", None, None), - ADMIN(Some(t)) => Message::new(None, "ADMIN", None, Some(t[])), + ADMIN(Some(t)) => Message::new(None, "ADMIN", None, Some(t)), ADMIN(None) => Message::new(None, "ADMIN", None, None), - INFO(Some(t)) => Message::new(None, "INFO", None, Some(t[])), + INFO(Some(t)) => Message::new(None, "INFO", None, Some(t)), INFO(None) => Message::new(None, "INFO", None, None), - SERVLIST(Some(m), Some(t)) => Message::new(None, "SERVLIST", Some(vec![m[]]), Some(t[])), - SERVLIST(Some(m), None) => Message::new(None, "SERVLIST", Some(vec![m[]]), None), + SERVLIST(Some(m), Some(t)) => Message::new(None, "SERVLIST", Some(vec![m]), Some(t)), + SERVLIST(Some(m), None) => Message::new(None, "SERVLIST", Some(vec![m]), None), SERVLIST(None, _) => Message::new(None, "SERVLIST", None, None), - SQUERY(s, t) => Message::new(None, "SQUERY", Some(vec![s[], t[]]), None), - WHO(Some(s), Some(true)) => Message::new(None, "WHO", Some(vec![s[], "o"]), None), - WHO(Some(s), _) => Message::new(None, "WHO", Some(vec![s[]]), None), + SQUERY(s, t) => Message::new(None, "SQUERY", Some(vec![s, t]), None), + WHO(Some(s), Some(true)) => Message::new(None, "WHO", Some(vec![s, "o"]), None), + WHO(Some(s), _) => Message::new(None, "WHO", Some(vec![s]), None), WHO(None, _) => Message::new(None, "WHO", None, None), - WHOIS(Some(t), m) => Message::new(None, "WHOIS", Some(vec![t[], m[]]), None), - WHOIS(None, m) => Message::new(None, "WHOIS", Some(vec![m[]]), None), - WHOWAS(n, Some(c), Some(t)) => Message::new(None, "WHOWAS", Some(vec![n[], c[]]), Some(t[])), - WHOWAS(n, Some(c), None) => Message::new(None, "WHOWAS", Some(vec![n[], c[]]), None), - WHOWAS(n, None, _) => Message::new(None, "WHOWAS", Some(vec![n[]]), None), - KILL(n, c) => Message::new(None, "KILL", Some(vec![n[]]), Some(c[])), - PING(s, Some(t)) => Message::new(None, "PING", Some(vec![s[]]), Some(t[])), - PING(s, None) => Message::new(None, "PING", None, Some(s[])), - PONG(s, Some(t)) => Message::new(None, "PONG", Some(vec![s[]]), Some(t[])), - PONG(s, None) => Message::new(None, "PONG", None, Some(s[])), - ERROR(m) => Message::new(None, "ERROR", None, Some(m[])), - AWAY(Some(m)) => Message::new(None, "AWAY", None, Some(m[])), + WHOIS(Some(t), m) => Message::new(None, "WHOIS", Some(vec![t, m]), None), + WHOIS(None, m) => Message::new(None, "WHOIS", Some(vec![m]), None), + WHOWAS(n, Some(c), Some(t)) => Message::new(None, "WHOWAS", Some(vec![n, c]), Some(t)), + WHOWAS(n, Some(c), None) => Message::new(None, "WHOWAS", Some(vec![n, c]), None), + WHOWAS(n, None, _) => Message::new(None, "WHOWAS", Some(vec![n]), None), + KILL(n, c) => Message::new(None, "KILL", Some(vec![n]), Some(c)), + PING(s, Some(t)) => Message::new(None, "PING", Some(vec![s]), Some(t)), + PING(s, None) => Message::new(None, "PING", None, Some(s)), + PONG(s, Some(t)) => Message::new(None, "PONG", Some(vec![s]), Some(t)), + PONG(s, None) => Message::new(None, "PONG", None, Some(s)), + ERROR(m) => Message::new(None, "ERROR", None, Some(m)), + AWAY(Some(m)) => Message::new(None, "AWAY", None, Some(m)), AWAY(None) => Message::new(None, "AWAY", None, None), REHASH => Message::new(None, "REHASH", None, None), DIE => Message::new(None, "DIE", None, None), RESTART => Message::new(None, "RESTART", None, None), - SUMMON(u, Some(t), Some(c)) => Message::new(None, "SUMMON", Some(vec![u[], t[]]), Some(c[])), - SUMMON(u, Some(t), None) => Message::new(None, "SUMMON", Some(vec![u[], t[]]), None), - SUMMON(u, None, _) => Message::new(None, "SUMMON", Some(vec![u[]]), None), - USERS(Some(t)) => Message::new(None, "USERS", None, Some(t[])), + SUMMON(u, Some(t), Some(c)) => Message::new(None, "SUMMON", Some(vec![u, t]), Some(c)), + SUMMON(u, Some(t), None) => Message::new(None, "SUMMON", Some(vec![u, t]), None), + SUMMON(u, None, _) => Message::new(None, "SUMMON", Some(vec![u]), None), + USERS(Some(t)) => Message::new(None, "USERS", None, Some(t)), USERS(None) => Message::new(None, "USERS", None, None), - WALLOPS(t) => Message::new(None, "WALLOPS", None, Some(t[])), - USERHOST(u) => Message::new(None, "USERHOST", Some(u.iter().map(|s| s[]).collect()), None), - ISON(u) => Message::new(None, "ISON", Some(u.iter().map(|s| s[]).collect()), None), - SAJOIN(n, c) => Message::new(None, "SAJOIN", Some(vec![n[], c[]]), None), - SAMODE(t, m, Some(p)) => Message::new(None, "SAMODE", Some(vec![t[], m[], p[]]), None), - SAMODE(t, m, None) => Message::new(None, "SAMODE", Some(vec![t[], m[]]), None), - SANICK(o, n) => Message::new(None, "SANICK", Some(vec![o[], n[]]), None), - SAPART(c, r) => Message::new(None, "SAPART", Some(vec![c[]]), Some(r[])), - SAQUIT(c, r) => Message::new(None, "SAQUIT", Some(vec![c[]]), Some(r[])), + WALLOPS(t) => Message::new(None, "WALLOPS", None, Some(t)), + USERHOST(u) => Message::new(None, "USERHOST", Some(u), None), + ISON(u) => Message::new(None, "ISON", Some(u), None), + SAJOIN(n, c) => Message::new(None, "SAJOIN", Some(vec![n, c]), None), + SAMODE(t, m, Some(p)) => Message::new(None, "SAMODE", Some(vec![t, m, p]), None), + SAMODE(t, m, None) => Message::new(None, "SAMODE", Some(vec![t, m]), None), + SANICK(o, n) => Message::new(None, "SANICK", Some(vec![o, n]), None), + SAPART(c, r) => Message::new(None, "SAPART", Some(vec![c]), Some(r)), + SAQUIT(c, r) => Message::new(None, "SAQUIT", Some(vec![c]), Some(r)), } } /// Converts a Message into a Command #[stable] - pub fn from_message(m: Message) -> IoResult { + pub fn from_message(m: &'a Message) -> IoResult> { Ok(if let "PASS" = m.command[] { if m.suffix.is_some() { if m.args.len() != 0 { return Err(invalid_input()) } - PASS(m.suffix.unwrap()) + PASS(m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 1 { return Err(invalid_input()) } - PASS(m.args[0].clone()) + PASS(m.args[0][]) } } else if let "NICK" = m.command[] { if m.suffix.is_some() { if m.args.len() != 0 { return Err(invalid_input()) } - NICK(m.suffix.unwrap()) + NICK(m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 1 { return Err(invalid_input()) } - NICK(m.args[0].clone()) + NICK(m.args[0][]) } } else if let "USER" = m.command[] { if m.suffix.is_some() { if m.args.len() != 2 { return Err(invalid_input()) } - USER(m.args[0].clone(), m.args[1].clone(), m.suffix.unwrap().clone()) + USER(m.args[0][], m.args[1][], m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 3 { return Err(invalid_input()) } - USER(m.args[0].clone(), m.args[1].clone(), m.args[2].clone()) + USER(m.args[0][], m.args[1][], m.args[2][]) } } else if let "OPER" = m.command[] { if m.suffix.is_some() { if m.args.len() != 1 { return Err(invalid_input()) } - OPER(m.args[0].clone(), m.suffix.unwrap()) + OPER(m.args[0][], m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 2 { return Err(invalid_input()) } - OPER(m.args[0].clone(), m.args[1].clone()) + OPER(m.args[0][], m.args[1][]) } } else if let "MODE" = m.command[] { if m.suffix.is_some() { if m.args.len() != 2 { return Err(invalid_input()) } - MODE(m.args[0].clone(), m.args[1].clone(), Some(m.suffix.unwrap().clone())) + MODE(m.args[0][], m.args[1][], Some(m.suffix.as_ref().unwrap()[])) } else if m.args.len() == 3 { - MODE(m.args[0].clone(), m.args[1].clone(), Some(m.args[2].clone())) + MODE(m.args[0][], m.args[1][], Some(m.args[2][])) } else if m.args.len() == 2 { - MODE(m.args[0].clone(), m.args[1].clone(), None) + MODE(m.args[0][], m.args[1][], None) } else { return Err(invalid_input()) } } else if let "SERVICE" = m.command[] { if m.suffix.is_some() { if m.args.len() != 5 { return Err(invalid_input()) } - SERVICE(m.args[0].clone(), m.args[1].clone(), m.args[2].clone(), m.args[3].clone(), - m.args[4].clone(), m.suffix.unwrap().clone()) + SERVICE(m.args[0][], m.args[1][], m.args[2][], m.args[3][], m.args[4][], m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 6 { return Err(invalid_input()) } - SERVICE(m.args[0].clone(), m.args[1].clone(), m.args[2].clone(), m.args[3].clone(), - m.args[4].clone(), m.args[5].clone()) + SERVICE(m.args[0][], m.args[1][], m.args[2][], m.args[3][], m.args[4][], m.args[5][]) } } else if let "QUIT" = m.command[] { if m.args.len() != 0 { return Err(invalid_input()) } if m.suffix.is_some() { - QUIT(Some(m.suffix.unwrap().clone())) + QUIT(Some(m.suffix.as_ref().unwrap()[])) } else { QUIT(None) } } else if let "SQUIT" = m.command[] { if m.suffix.is_some() { if m.args.len() != 1 { return Err(invalid_input()) } - SQUIT(m.args[0].clone(), m.suffix.unwrap().clone()) + SQUIT(m.args[0][], m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 2 { return Err(invalid_input()) } - SQUIT(m.args[0].clone(), m.args[1].clone()) + SQUIT(m.args[0][], m.args[1][]) } } else if let "JOIN" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - JOIN(m.suffix.unwrap().clone(), None) + JOIN(m.suffix.as_ref().unwrap()[], None) } else if m.args.len() == 1 { - JOIN(m.args[0].clone(), Some(m.suffix.unwrap().clone())) + JOIN(m.args[0][], Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 1 { - JOIN(m.args[0].clone(), None) + JOIN(m.args[0][], None) } else if m.args.len() == 2 { - JOIN(m.args[0].clone(), Some(m.args[1].clone())) + JOIN(m.args[0][], Some(m.args[1][])) } else { return Err(invalid_input()) } } else if let "PART" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - PART(m.suffix.unwrap().clone(), None) + PART(m.suffix.as_ref().unwrap()[], None) } else if m.args.len() == 1 { - PART(m.args[0].clone(), Some(m.suffix.unwrap().clone())) + PART(m.args[0][], Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 1 { - PART(m.args[0].clone(), None) + PART(m.args[0][], None) } else if m.args.len() == 2 { - PART(m.args[0].clone(), Some(m.args[1].clone())) + PART(m.args[0][], Some(m.args[1][])) } else { return Err(invalid_input()) } } else if let "TOPIC" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - TOPIC(m.suffix.unwrap().clone(), None) + TOPIC(m.suffix.as_ref().unwrap()[], None) } else if m.args.len() == 1 { - TOPIC(m.args[0].clone(), Some(m.suffix.unwrap().clone())) + TOPIC(m.args[0][], Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 1 { - TOPIC(m.args[0].clone(), None) + TOPIC(m.args[0][], None) } else if m.args.len() == 2 { - TOPIC(m.args[0].clone(), Some(m.args[1].clone())) + TOPIC(m.args[0][], Some(m.args[1][])) } else { return Err(invalid_input()) } } else if let "NAMES" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - NAMES(Some(m.suffix.unwrap().clone()), None) + NAMES(Some(m.suffix.as_ref().unwrap()[]), None) } else if m.args.len() == 1 { - NAMES(Some(m.args[0].clone()), Some(m.suffix.unwrap().clone())) + NAMES(Some(m.args[0][]), Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 0 { NAMES(None, None) } else if m.args.len() == 1 { - NAMES(Some(m.args[0].clone()), None) + NAMES(Some(m.args[0][]), None) } else if m.args.len() == 2 { - NAMES(Some(m.args[0].clone()), Some(m.args[1].clone())) + NAMES(Some(m.args[0][]), Some(m.args[1][])) } else { return Err(invalid_input()) } } else if let "LIST" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - LIST(Some(m.suffix.unwrap().clone()), None) + LIST(Some(m.suffix.as_ref().unwrap()[]), None) } else if m.args.len() == 1 { - LIST(Some(m.args[0].clone()), Some(m.suffix.unwrap().clone())) + LIST(Some(m.args[0][]), Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 0 { LIST(None, None) } else if m.args.len() == 1 { - LIST(Some(m.args[0].clone()), None) + LIST(Some(m.args[0][]), None) } else if m.args.len() == 2 { - LIST(Some(m.args[0].clone()), Some(m.args[1].clone())) + LIST(Some(m.args[0][]), Some(m.args[1][])) } else { return Err(invalid_input()) } } else if let "INVITE" = m.command[] { if m.suffix.is_some() { if m.args.len() != 1 { return Err(invalid_input()) } - INVITE(m.args[0].clone(), m.suffix.unwrap()) + INVITE(m.args[0][], m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 2 { return Err(invalid_input()) } - INVITE(m.args[0].clone(), m.args[1].clone()) + INVITE(m.args[0][], m.args[1][]) } } else if let "KICK" = m.command[] { if m.args.len() != 2 { return Err(invalid_input()) } - KICK(m.args[0].clone(), m.args[1].clone(), m.suffix.clone()) + KICK(m.args[0][], m.args[1][], m.suffix.as_ref().map(|s| s[])) } else if let "PRIVMSG" = m.command[] { if !m.suffix.is_some() || m.args.len() != 1 { return Err(invalid_input()) } - PRIVMSG(m.args[0].clone(), m.suffix.unwrap().clone()) + PRIVMSG(m.args[0][], m.suffix.as_ref().unwrap()[]) } else if let "NOTICE" = m.command[] { if !m.suffix.is_some() || m.args.len() != 1 { return Err(invalid_input()) } - NOTICE(m.args[0].clone(), m.suffix.unwrap().clone()) + NOTICE(m.args[0][], m.suffix.as_ref().unwrap()[]) } else if let "MOTD" = m.command[] { if m.args.len() != 0 { return Err(invalid_input()) } if m.suffix.is_some() { - MOTD(Some(m.suffix.unwrap().clone())) + MOTD(Some(m.suffix.as_ref().unwrap()[])) } else { MOTD(None) } } else if let "LUSERS" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - LUSERS(Some(m.suffix.unwrap().clone()), None) + LUSERS(Some(m.suffix.as_ref().unwrap()[]), None) } else if m.args.len() == 1 { - LUSERS(Some(m.args[0].clone()), Some(m.suffix.unwrap().clone())) + LUSERS(Some(m.args[0][]), Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 0 { LUSERS(None, None) } else if m.args.len() == 1 { - LUSERS(Some(m.args[0].clone()), None) + LUSERS(Some(m.args[0][]), None) } else if m.args.len() == 2 { - LUSERS(Some(m.args[0].clone()), Some(m.args[1].clone())) + LUSERS(Some(m.args[0][]), Some(m.args[1][])) } else { return Err(invalid_input()) } } else if let "VERSION" = m.command[] { if m.args.len() != 0 { return Err(invalid_input()) } if m.suffix.is_some() { - VERSION(Some(m.suffix.unwrap().clone())) + VERSION(Some(m.suffix.as_ref().unwrap()[])) } else { VERSION(None) } } else if let "STATS" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - STATS(Some(m.suffix.unwrap().clone()), None) + STATS(Some(m.suffix.as_ref().unwrap()[]), None) } else if m.args.len() == 1 { - STATS(Some(m.args[0].clone()), Some(m.suffix.unwrap().clone())) + STATS(Some(m.args[0][]), Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 0 { STATS(None, None) } else if m.args.len() == 1 { - STATS(Some(m.args[0].clone()), None) + STATS(Some(m.args[0][]), None) } else if m.args.len() == 2 { - STATS(Some(m.args[0].clone()), Some(m.args[1].clone())) + STATS(Some(m.args[0][]), Some(m.args[1][])) } else { return Err(invalid_input()) } } else if let "LINKS" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - LINKS(None, Some(m.suffix.unwrap().clone())) + LINKS(None, Some(m.suffix.as_ref().unwrap()[])) } else if m.args.len() == 1 { - LINKS(Some(m.args[0].clone()), Some(m.suffix.unwrap().clone())) + LINKS(Some(m.args[0][]), Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } @@ -465,163 +463,163 @@ impl Command { } else if let "TIME" = m.command[] { if m.args.len() != 0 { return Err(invalid_input()) } if m.suffix.is_some() { - TIME(Some(m.suffix.unwrap().clone())) + TIME(Some(m.suffix.as_ref().unwrap()[])) } else { TIME(None) } } else if let "CONNECT" = m.command[] { if m.args.len() != 2 { return Err(invalid_input()) } - KICK(m.args[0].clone(), m.args[1].clone(), m.suffix.clone()) + CONNECT(m.args[0][], m.args[1][], m.suffix.as_ref().map(|s| s[])) } else if let "TRACE" = m.command[] { if m.args.len() != 0 { return Err(invalid_input()) } if m.suffix.is_some() { - TRACE(Some(m.suffix.unwrap().clone())) + TRACE(Some(m.suffix.as_ref().unwrap()[])) } else { TRACE(None) } } else if let "ADMIN" = m.command[] { if m.args.len() != 0 { return Err(invalid_input()) } if m.suffix.is_some() { - TIME(Some(m.suffix.unwrap().clone())) + TIME(Some(m.suffix.as_ref().unwrap()[])) } else { TIME(None) } } else if let "INFO" = m.command[] { if m.args.len() != 0 { return Err(invalid_input()) } if m.suffix.is_some() { - TIME(Some(m.suffix.unwrap().clone())) + TIME(Some(m.suffix.as_ref().unwrap()[])) } else { TIME(None) } } else if let "SERVLIST" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - SERVLIST(Some(m.suffix.unwrap().clone()), None) + SERVLIST(Some(m.suffix.as_ref().unwrap()[]), None) } else if m.args.len() == 1 { - SERVLIST(Some(m.args[0].clone()), Some(m.suffix.unwrap().clone())) + SERVLIST(Some(m.args[0][]), Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 0 { SERVLIST(None, None) } else if m.args.len() == 1 { - SERVLIST(Some(m.args[0].clone()), None) + SERVLIST(Some(m.args[0][]), None) } else if m.args.len() == 2 { - SERVLIST(Some(m.args[0].clone()), Some(m.args[1].clone())) + SERVLIST(Some(m.args[0][]), Some(m.args[1][])) } else { return Err(invalid_input()) } } else if let "SQUERY" = m.command[] { if m.suffix.is_some() { if m.args.len() != 1 { return Err(invalid_input()) } - SQUERY(m.args[0].clone(), m.suffix.unwrap()) + SQUERY(m.args[0][], m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 2 { return Err(invalid_input()) } - SQUERY(m.args[0].clone(), m.args[1].clone()) + SQUERY(m.args[0][], m.args[1][]) } } else if let "WHO" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - WHO(Some(m.suffix.unwrap().clone()), None) + WHO(Some(m.suffix.as_ref().unwrap()[]), None) } else if m.args.len() == 1 { - WHO(Some(m.args[0].clone()), Some(m.suffix.unwrap()[] == "o")) + WHO(Some(m.args[0][]), Some(m.suffix.as_ref().unwrap()[] == "o")) } else { return Err(invalid_input()) } } else if m.args.len() == 0 { WHO(None, None) } else if m.args.len() == 1 { - WHO(Some(m.args[0].clone()), None) + WHO(Some(m.args[0][]), None) } else if m.args.len() == 2 { - WHO(Some(m.args[0].clone()), Some(m.args[1][] == "o")) + WHO(Some(m.args[0][]), Some(m.args[1][] == "o")) } else { return Err(invalid_input()) } } else if let "WHOIS" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - WHOIS(None, m.suffix.unwrap().clone()) + WHOIS(None, m.suffix.as_ref().unwrap()[]) } else if m.args.len() == 1 { - WHOIS(Some(m.args[0].clone()), m.suffix.unwrap().clone()) + WHOIS(Some(m.args[0][]), m.suffix.as_ref().unwrap()[]) } else { return Err(invalid_input()) } } else if m.args.len() == 1 { - WHOIS(None, m.args[0].clone()) + WHOIS(None, m.args[0][]) } else if m.args.len() == 2 { - WHOIS(Some(m.args[0].clone()), m.args[1].clone()) + WHOIS(Some(m.args[0][]), m.args[1][]) } else { return Err(invalid_input()) } } else if let "WHOWAS" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - WHOWAS(m.suffix.unwrap().clone(), None, None) + WHOWAS(m.suffix.as_ref().unwrap()[], None, None) } else if m.args.len() == 1 { - WHOWAS(m.args[0].clone(), None, Some(m.suffix.unwrap().clone())) + WHOWAS(m.args[0][], None, Some(m.suffix.as_ref().unwrap()[])) } else if m.args.len() == 2 { - WHOWAS(m.args[0].clone(), Some(m.args[1].clone()), Some(m.suffix.unwrap().clone())) + WHOWAS(m.args[0][], Some(m.args[1][]), Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 1 { - WHOWAS(m.args[0].clone(), None, None) + WHOWAS(m.args[0][], None, None) } else if m.args.len() == 2 { - WHOWAS(m.args[0].clone(), None, Some(m.args[1].clone())) + WHOWAS(m.args[0][], None, Some(m.args[1][])) } else if m.args.len() == 3 { - WHOWAS(m.args[0].clone(), Some(m.args[1].clone()), Some(m.args[2].clone())) + WHOWAS(m.args[0][], Some(m.args[1][]), Some(m.args[2][])) } else { return Err(invalid_input()) } } else if let "KILL" = m.command[] { if m.suffix.is_some() { if m.args.len() != 1 { return Err(invalid_input()) } - KILL(m.args[0].clone(), m.suffix.unwrap()) + KILL(m.args[0][], m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 2 { return Err(invalid_input()) } - KILL(m.args[0].clone(), m.args[1].clone()) + KILL(m.args[0][], m.args[1][]) } } else if let "PING" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - PING(m.suffix.unwrap().clone(), None) + PING(m.suffix.as_ref().unwrap()[], None) } else if m.args.len() == 1 { - PING(m.args[0].clone(), Some(m.suffix.unwrap().clone())) + PING(m.args[0][], Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 1 { - PING(m.args[0].clone(), None) + PING(m.args[0][], None) } else if m.args.len() == 2 { - PING(m.args[0].clone(), Some(m.args[1].clone())) + PING(m.args[0][], Some(m.args[1][])) } else { return Err(invalid_input()) } } else if let "PONG" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - PONG(m.suffix.unwrap().clone(), None) + PONG(m.suffix.as_ref().unwrap()[], None) } else if m.args.len() == 1 { - PONG(m.args[0].clone(), Some(m.suffix.unwrap().clone())) + PONG(m.args[0][], Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 1 { - PONG(m.args[0].clone(), None) + PONG(m.args[0][], None) } else if m.args.len() == 2 { - PONG(m.args[0].clone(), Some(m.args[1].clone())) + PONG(m.args[0][], Some(m.args[1][])) } else { return Err(invalid_input()) } } else if let "ERROR" = m.command[] { if m.suffix.is_some() && m.args.len() == 0 { - ERROR(m.suffix.unwrap().clone()) + ERROR(m.suffix.as_ref().unwrap()[]) } else { return Err(invalid_input()) } } else if let "AWAY" = m.command[] { if m.args.len() == 0 { - AWAY(m.suffix.clone()) + AWAY(m.suffix.as_ref().map(|s| s[])) } else { return Err(invalid_input()) } @@ -646,98 +644,98 @@ impl Command { } else if let "SUMMON" = m.command[] { if m.suffix.is_some() { if m.args.len() == 0 { - SUMMON(m.suffix.unwrap().clone(), None, None) + SUMMON(m.suffix.as_ref().unwrap()[], None, None) } else if m.args.len() == 1 { - SUMMON(m.args[0].clone(), Some(m.suffix.unwrap().clone()), None) + SUMMON(m.args[0][], Some(m.suffix.as_ref().unwrap()[]), None) } else if m.args.len() == 2 { - SUMMON(m.args[0].clone(), Some(m.args[1].clone()), Some(m.suffix.unwrap().clone())) + SUMMON(m.args[0][], Some(m.args[1][]), Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 1 { - SUMMON(m.args[0].clone(), None, None) + SUMMON(m.args[0][], None, None) } else if m.args.len() == 2 { - SUMMON(m.args[0].clone(), Some(m.args[1].clone()), None) + SUMMON(m.args[0][], Some(m.args[1][]), None) } else if m.args.len() == 3 { - SUMMON(m.args[0].clone(), Some(m.args[1].clone()), Some(m.args[2].clone())) + SUMMON(m.args[0][], Some(m.args[1][]), Some(m.args[2][])) } else { return Err(invalid_input()) } } else if let "USERS" = m.command[] { if m.args.len() == 0 { - USERS(m.suffix.clone()) + USERS(m.suffix.as_ref().map(|s| s[])) } else if m.args.len() == 1 { - USERS(Some(m.args[0].clone())) + USERS(Some(m.args[0][])) } else { return Err(invalid_input()) } } else if let "WALLOPS" = m.command[] { if m.suffix.is_some() && m.args.len() == 0 { - WALLOPS(m.suffix.unwrap().clone()) + WALLOPS(m.suffix.as_ref().unwrap()[]) } else if m.args.len() == 1 { - WALLOPS(m.args[0].clone()) + WALLOPS(m.args[0][]) } else { return Err(invalid_input()) } } else if let "USERHOST" = m.command[] { if m.suffix.is_none() { - USERHOST(m.args.clone()) + USERHOST(m.args.iter().map(|s| s[]).collect()) } else { return Err(invalid_input()) } } else if let "ISON" = m.command[] { if m.suffix.is_none() { - USERHOST(m.args.clone()) + USERHOST(m.args.iter().map(|s| s[]).collect()) } else { return Err(invalid_input()) } } else if let "SAJOIN" = m.command[] { if m.suffix.is_some() { if m.args.len() != 1 { return Err(invalid_input()) } - SAJOIN(m.args[0].clone(), m.suffix.unwrap()) + SAJOIN(m.args[0][], m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 2 { return Err(invalid_input()) } - SAJOIN(m.args[0].clone(), m.args[1].clone()) + SAJOIN(m.args[0][], m.args[1][]) } } else if let "SAMODE" = m.command[] { if m.suffix.is_some() { if m.args.len() == 1 { - SAMODE(m.args[0].clone(), m.suffix.unwrap().clone(), None) + SAMODE(m.args[0][], m.suffix.as_ref().unwrap()[], None) } else if m.args.len() == 2 { - SAMODE(m.args[0].clone(), m.args[1].clone(), Some(m.suffix.unwrap().clone())) + SAMODE(m.args[0][], m.args[1][], Some(m.suffix.as_ref().unwrap()[])) } else { return Err(invalid_input()) } } else if m.args.len() == 2 { - SAMODE(m.args[0].clone(), m.args[1].clone(), None) + SAMODE(m.args[0][], m.args[1][], None) } else if m.args.len() == 3 { - SAMODE(m.args[0].clone(), m.args[1].clone(), Some(m.args[2].clone())) + SAMODE(m.args[0][], m.args[1][], Some(m.args[2][])) } else { return Err(invalid_input()) } } else if let "SANICK" = m.command[] { if m.suffix.is_some() { if m.args.len() != 1 { return Err(invalid_input()) } - SANICK(m.args[0].clone(), m.suffix.unwrap()) + SANICK(m.args[0][], m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 2 { return Err(invalid_input()) } - SANICK(m.args[0].clone(), m.args[1].clone()) + SANICK(m.args[0][], m.args[1][]) } } else if let "SAPART" = m.command[] { if m.suffix.is_some() { if m.args.len() != 1 { return Err(invalid_input()) } - SAPART(m.args[0].clone(), m.suffix.unwrap()) + SAPART(m.args[0][], m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 2 { return Err(invalid_input()) } - SAPART(m.args[0].clone(), m.args[1].clone()) + SAPART(m.args[0][], m.args[1][]) } } else if let "SAQUIT" = m.command[] { if m.suffix.is_some() { if m.args.len() != 1 { return Err(invalid_input()) } - SAQUIT(m.args[0].clone(), m.suffix.unwrap()) + SAQUIT(m.args[0][], m.suffix.as_ref().unwrap()[]) } else { if m.args.len() != 2 { return Err(invalid_input()) } - SAQUIT(m.args[0].clone(), m.args[1].clone()) + SAQUIT(m.args[0][], m.args[1][]) } } else { return Err(invalid_input()) From 4d7c2065e98c80cb8e85a9a5847c16f70acc0610 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Mon, 3 Nov 2014 01:47:36 -0500 Subject: [PATCH 11/18] Added FIXME to from_message(...) with details on how to rewrite it. --- src/data/command.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/data/command.rs b/src/data/command.rs index 1fee292..071a36c 100644 --- a/src/data/command.rs +++ b/src/data/command.rs @@ -229,6 +229,20 @@ impl<'a> Command<'a> { /// Converts a Message into a Command #[stable] pub fn from_message(m: &'a Message) -> IoResult> { + /* FIXME: Re-write this using match as so: + if let "PASS" = m.command[] { + match m.suffix { + Some(ref suffix) => { + if m.args.len() != 0 { return Err(invalid_input()) } + PASS(suffix[]) + }, + None => { + if m.args.len() != 1 { return Err(invalid_input()) } + PASS(m.args[0][]) + } + } + } + */ Ok(if let "PASS" = m.command[] { if m.suffix.is_some() { if m.args.len() != 0 { return Err(invalid_input()) } From 42b4dcbf03ccbf92dfa9575ed95b4ece4eb66af3 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Mon, 3 Nov 2014 02:02:29 -0500 Subject: [PATCH 12/18] Added identify(...) utility, and clarified intent in some places using match. --- src/data/message.rs | 11 ++++++----- src/server/mod.rs | 6 ++++-- src/server/utils.rs | 11 +++++++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/data/message.rs b/src/data/message.rs index dac1b8a..ce1680c 100644 --- a/src/data/message.rs +++ b/src/data/message.rs @@ -70,11 +70,12 @@ impl FromStr for Message { } else { None }; - let command = if let Some(cmd) = state.find(' ').map(|i| s[..i]) { - state = state.find(' ').map_or("", |i| s[i..]); - cmd - } else { - return None + let command = match state.find(' ').map(|i| s[..i]) { + Some(cmd) => { + state = state.find(' ').map_or("", |i| s[i..]); + cmd + } + _ => return None }; let args: Vec<_> = state.splitn(14, ' ').collect(); Some(Message::new(prefix, command, if args.len() > 0 { Some(args) } else { None }, suffix)) diff --git a/src/server/mod.rs b/src/server/mod.rs index 3ed1321..38456ba 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -86,7 +86,9 @@ impl<'a, T, U> ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader { impl<'a, T, U> Iterator for ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader { fn next(&mut self) -> Option { let line = self.server.conn.recv(); - if let Err(_) = line { return None } - from_str(line.unwrap()[]) + match line { + Err(_) => None, + Ok(msg) => from_str(msg[]) + } } } diff --git a/src/server/utils.rs b/src/server/utils.rs index 07cdcfd..8ced1c6 100644 --- a/src/server/utils.rs +++ b/src/server/utils.rs @@ -1,2 +1,13 @@ //! Utilities and shortcuts for working with IRC servers #![experimental] + +use std::io::IoResult; +use data::command::{NICK, USER}; +use data::kinds::{IrcReader, IrcWriter}; +use server::Server; + +/// Sends a NICK and USER to identify +pub fn identify<'a, T, U>(server: &Server<'a, T, U>) -> IoResult<()> where T: IrcWriter, U: IrcReader { + try!(server.send(NICK(server.config().nickname[]))); + server.send(USER(server.config().username[], "0", server.config().realname[])) +} From c20038951e1f188eca400bf9e7377903bf9e4bf7 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Mon, 3 Nov 2014 02:11:51 -0500 Subject: [PATCH 13/18] Added simple example, and changed how IrcServer creation works. --- examples/simple.rs | 26 ++++++++++++++++++++++++++ src/server/mod.rs | 18 +++++++++++------- 2 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 examples/simple.rs diff --git a/examples/simple.rs b/examples/simple.rs new file mode 100644 index 0000000..bf2de9f --- /dev/null +++ b/examples/simple.rs @@ -0,0 +1,26 @@ +#![feature(slicing_syntax)] +extern crate irc; + +use std::collections::HashMap; +use irc::data::config::Config; +use irc::server::{IrcServer, Server}; +use irc::server::utils::identify; + +fn main() { + let config = Config { + owners: vec!("awe".into_string()), + nickname: "pickles".into_string(), + username: "pickles".into_string(), + realname: "pickles".into_string(), + password: "".into_string(), + server: "irc.fyrechat.net".into_string(), + port: 6667, + channels: vec!("#vana".into_string()), + options: HashMap::new(), + }; + let server = IrcServer::from_config(config).unwrap(); + identify(&server).unwrap(); + for message in server.iter() { + println!("RCV: {}", message); + } +} diff --git a/src/server/mod.rs b/src/server/mod.rs index 38456ba..483d820 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -30,15 +30,19 @@ pub struct IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { } impl<'a> IrcServer<'a, BufferedWriter, BufferedReader> { - /// Creates a new IRC Server connection from the specified configuration, connecting immediately. + /// Creates a new IRC Server connection from the configuration at the specified path, connecting immediately. #[experimental] pub fn new(config: &str) -> IoResult, BufferedReader>> { let config = try!(Config::load_utf8(config)); let conn = try!(Connection::connect(config.server[], config.port)); - Ok(IrcServer { - conn: conn, - config: config - }) + IrcServer::from_connection(config, conn) + } + + /// Creates a new IRC server connection from the specified configuration, connecting immediately. + #[experimental] + pub fn from_config(config: Config) -> IoResult, BufferedReader>> { + let conn = try!(Connection::connect(config.server[], config.port)); + IrcServer::from_connection(config, conn) } } @@ -59,10 +63,10 @@ impl<'a, T, U> Server<'a, T, U> for IrcServer<'a, T, U> where T: IrcWriter, U: I impl<'a, T, U> IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { /// Creates an IRC server from the specified configuration, and any arbitrary Connection #[experimental] - pub fn from_connection(config: &str, conn: Connection) -> IoResult> { + pub fn from_connection(config: Config, conn: Connection) -> IoResult> { Ok(IrcServer { conn: conn, - config: try!(Config::load_utf8(config)) + config: config }) } } From c8dc582fd122372c62e7b3bbb3bbf20465b37372 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Mon, 3 Nov 2014 02:30:58 -0500 Subject: [PATCH 14/18] Fixed bug with message parsing, and updated example. --- examples/simple.rs | 2 +- src/data/message.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index bf2de9f..09cf6b8 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -21,6 +21,6 @@ fn main() { let server = IrcServer::from_config(config).unwrap(); identify(&server).unwrap(); for message in server.iter() { - println!("RCV: {}", message); + println!("{}", message) } } diff --git a/src/data/message.rs b/src/data/message.rs index ce1680c..bce8057 100644 --- a/src/data/message.rs +++ b/src/data/message.rs @@ -57,27 +57,27 @@ impl FromStr for Message { let mut state = s.clone(); if s.len() == 0 { return None } let prefix = if state.starts_with(":") { - let prefix = state.find(' ').map(|i| s[1..i]); - state = state.find(' ').map_or("", |i| s[i..]); + let prefix = state.find(' ').map(|i| state[1..i]); + state = state.find(' ').map_or("", |i| state[i+1..]); prefix } else { None }; let suffix = if state.contains(":") { - let suffix = state.find(':').map(|i| s[i..]); - state = state.find(':').map_or("", |i| s[..i]); + let suffix = state.find(':').map(|i| state[i..state.len()-1]); + state = state.find(':').map_or("", |i| state[..i]); suffix } else { None }; - let command = match state.find(' ').map(|i| s[..i]) { + let command = match state.find(' ').map(|i| state[..i]) { Some(cmd) => { - state = state.find(' ').map_or("", |i| s[i..]); + state = state.find(' ').map_or("", |i| state[i+1..]); cmd } _ => return None }; - let args: Vec<_> = state.splitn(14, ' ').collect(); + let args: Vec<_> = state.splitn(14, ' ').filter(|s| s.len() != 0).collect(); Some(Message::new(prefix, command, if args.len() > 0 { Some(args) } else { None }, suffix)) } } From 4479c59e79ad8f96d3c3196c151c76896b7c81e8 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Mon, 3 Nov 2014 02:42:06 -0500 Subject: [PATCH 15/18] Fixed bug with Message parsing causing suffixes to include colon prefix. --- examples/simple.rs | 2 +- src/data/message.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index 09cf6b8..fbd7e7e 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -21,6 +21,6 @@ fn main() { let server = IrcServer::from_config(config).unwrap(); identify(&server).unwrap(); for message in server.iter() { - println!("{}", message) + println!("{}", message.into_string()) } } diff --git a/src/data/message.rs b/src/data/message.rs index bce8057..3019749 100644 --- a/src/data/message.rs +++ b/src/data/message.rs @@ -64,7 +64,7 @@ impl FromStr for Message { None }; let suffix = if state.contains(":") { - let suffix = state.find(':').map(|i| state[i..state.len()-1]); + let suffix = state.find(':').map(|i| state[i+1..state.len()-1]); state = state.find(':').map_or("", |i| state[..i]); suffix } else { From 16f29f179a1fb75535a006e70c00365379e1a38e Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Mon, 3 Nov 2014 02:54:40 -0500 Subject: [PATCH 16/18] Added basic message handling, and fixed a bug in message parsing. --- examples/simple.rs | 2 +- src/data/message.rs | 2 +- src/server/mod.rs | 16 ++++++++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index fbd7e7e..81d708c 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -21,6 +21,6 @@ fn main() { let server = IrcServer::from_config(config).unwrap(); identify(&server).unwrap(); for message in server.iter() { - println!("{}", message.into_string()) + println!("{}", message); } } diff --git a/src/data/message.rs b/src/data/message.rs index 3019749..022e469 100644 --- a/src/data/message.rs +++ b/src/data/message.rs @@ -64,7 +64,7 @@ impl FromStr for Message { None }; let suffix = if state.contains(":") { - let suffix = state.find(':').map(|i| state[i+1..state.len()-1]); + let suffix = state.find(':').map(|i| state[i+1..state.len()-2]); state = state.find(':').map_or("", |i| state[..i]); suffix } else { diff --git a/src/server/mod.rs b/src/server/mod.rs index 483d820..37184a1 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -2,7 +2,7 @@ #![experimental] use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; use conn::Connection; -use data::command::Command; +use data::command::{Command, PONG}; use data::config::Config; use data::kinds::{IrcReader, IrcWriter}; use data::message::Message; @@ -69,6 +69,14 @@ impl<'a, T, U> IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { config: config }) } + + fn handle_message(&self, message: &Message) { + if message.command[] == "PING" { + self.send(PONG(message.args[0][], None)).unwrap(); + println!("PING! PONG!"); + } + /* TODO: implement more message handling */ + } } /// An Iterator over an IrcServer's incoming Messages @@ -92,7 +100,11 @@ impl<'a, T, U> Iterator for ServerIterator<'a, T, U> where T: IrcWriter let line = self.server.conn.recv(); match line { Err(_) => None, - Ok(msg) => from_str(msg[]) + Ok(msg) => { + let message = from_str(msg[]); + self.server.handle_message(message.as_ref().unwrap()); + message + } } } } From cd52c5b6a4fa5b6706a0f18e3bf5686847bfda31 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Mon, 3 Nov 2014 03:02:35 -0500 Subject: [PATCH 17/18] Messages now properly end with new-lines. --- src/data/message.rs | 1 + src/server/mod.rs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/message.rs b/src/data/message.rs index 022e469..7039608 100644 --- a/src/data/message.rs +++ b/src/data/message.rs @@ -48,6 +48,7 @@ impl Message { ret.push_str(" :"); ret.push_str(suffix[]); } + ret.push_str("\r\n"); ret } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 37184a1..05a3f7c 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -72,8 +72,7 @@ impl<'a, T, U> IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { fn handle_message(&self, message: &Message) { if message.command[] == "PING" { - self.send(PONG(message.args[0][], None)).unwrap(); - println!("PING! PONG!"); + self.send(PONG(message.suffix.as_ref().unwrap()[], None)).unwrap(); } /* TODO: implement more message handling */ } From c7a0d4b83d80098da857c43ffd053c1ee238a767 Mon Sep 17 00:00:00 2001 From: Aaron Weiss Date: Mon, 3 Nov 2014 03:11:14 -0500 Subject: [PATCH 18/18] Implemented join on connection. --- examples/simple.rs | 2 +- src/server/mod.rs | 8 ++++++-- src/server/utils.rs | 10 +++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index 81d708c..9cc950a 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -21,6 +21,6 @@ fn main() { let server = IrcServer::from_config(config).unwrap(); identify(&server).unwrap(); for message in server.iter() { - println!("{}", message); + print!("{}", message.into_string()); } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 05a3f7c..249e09c 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -2,7 +2,7 @@ #![experimental] use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; use conn::Connection; -use data::command::{Command, PONG}; +use data::command::Command; use data::config::Config; use data::kinds::{IrcReader, IrcWriter}; use data::message::Message; @@ -72,7 +72,11 @@ impl<'a, T, U> IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { fn handle_message(&self, message: &Message) { if message.command[] == "PING" { - self.send(PONG(message.suffix.as_ref().unwrap()[], None)).unwrap(); + utils::send_pong(self, message.suffix.as_ref().unwrap()[]).unwrap(); + } else if message.command[] == "376" || message.command[] == "422" { + for chan in self.config.channels.iter() { + utils::send_join(self, chan[]).unwrap(); + } } /* TODO: implement more message handling */ } diff --git a/src/server/utils.rs b/src/server/utils.rs index 8ced1c6..2e44829 100644 --- a/src/server/utils.rs +++ b/src/server/utils.rs @@ -2,7 +2,7 @@ #![experimental] use std::io::IoResult; -use data::command::{NICK, USER}; +use data::command::{JOIN, NICK, PONG, USER}; use data::kinds::{IrcReader, IrcWriter}; use server::Server; @@ -11,3 +11,11 @@ pub fn identify<'a, T, U>(server: &Server<'a, T, U>) -> IoResult<()> where T: Ir try!(server.send(NICK(server.config().nickname[]))); server.send(USER(server.config().username[], "0", server.config().realname[])) } + +pub fn send_pong<'a, T, U>(server: &Server<'a, T, U>, msg: &str) -> IoResult<()> where T: IrcWriter, U: IrcReader { + server.send(PONG(msg, None)) +} + +pub fn send_join<'a, T, U>(server: &Server<'a, T, U>, chanlist: &str) -> IoResult<()> where T: IrcWriter, U: IrcReader { + server.send(JOIN(chanlist, None)) +}