diff --git a/examples/autoreconnect.rs b/examples/autoreconnect.rs index ab21160..0743ff1 100644 --- a/examples/autoreconnect.rs +++ b/examples/autoreconnect.rs @@ -1,5 +1,4 @@ -#![allow(unstable)] -#![feature(slicing_syntax)] +#![feature(core, slicing_syntax, std_misc)] extern crate irc; use std::default::Default; diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs index 9c06ff7..8832e46 100644 --- a/examples/multithreaded.rs +++ b/examples/multithreaded.rs @@ -1,5 +1,4 @@ -#![allow(unstable)] -#![feature(slicing_syntax)] +#![feature(core, slicing_syntax, std_misc)] extern crate irc; use std::default::Default; diff --git a/examples/simple.rs b/examples/simple.rs index 1c2ff52..8437834 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,5 +1,4 @@ -#![allow(unstable)] -#![feature(slicing_syntax)] +#![feature(core, slicing_syntax)] extern crate irc; use std::default::Default; diff --git a/examples/simple_ssl.rs b/examples/simple_ssl.rs index 545c2b4..a092293 100644 --- a/examples/simple_ssl.rs +++ b/examples/simple_ssl.rs @@ -1,5 +1,4 @@ -#![allow(unstable)] -#![feature(slicing_syntax)] +#![feature(core, slicing_syntax)] extern crate irc; use std::default::Default; diff --git a/examples/tweeter.rs b/examples/tweeter.rs index 4714408..dbce121 100644 --- a/examples/tweeter.rs +++ b/examples/tweeter.rs @@ -1,5 +1,4 @@ -#![feature(slicing_syntax)] -#![allow(unstable)] +#![feature(io, slicing_syntax, std_misc)] extern crate irc; use std::default::Default; diff --git a/src/client/data/command.rs b/src/client/data/command.rs index 1e9b551..a0f83e4 100644 --- a/src/client/data/command.rs +++ b/src/client/data/command.rs @@ -9,7 +9,7 @@ use client::data::message::{Message, ToMessage}; /// [capabilities extension](https://tools.ietf.org/html/draft-mitchell-irc-capabilities-01). /// Additionally, this includes some common additional commands from popular IRCds. #[stable] -#[derive(Show, PartialEq)] +#[derive(Debug, PartialEq)] pub enum Command<'a> { // 3.1 Connection Registration /// PASS :password @@ -992,7 +992,7 @@ impl<'a> Command<'a> { } } else if let "CAP" = &m.command[] { if m.args.len() != 1 { return Err(invalid_input()) } - if let Some(cmd) = m.args[0].parse() { + if let Ok(cmd) = m.args[0].parse() { match m.suffix { Some(ref suffix) => Command::CAP(cmd, Some(&suffix[])), None => Command::CAP(cmd, None), @@ -1008,7 +1008,7 @@ impl<'a> Command<'a> { /// A list of all of the subcommands for the capabilities extension. #[stable] -#[derive(Copy, Show, PartialEq)] +#[derive(Copy, Debug, PartialEq)] pub enum CapSubCommand { /// Requests a list of the server's capabilities. #[stable] @@ -1051,16 +1051,17 @@ impl CapSubCommand { } impl FromStr for CapSubCommand { - fn from_str(s: &str) -> Option { + type Err = &'static str; + fn from_str(s: &str) -> Result { match s { - "LS" => Some(CapSubCommand::LS), - "LIST" => Some(CapSubCommand::LIST), - "REQ" => Some(CapSubCommand::REQ), - "ACK" => Some(CapSubCommand::ACK), - "NAK" => Some(CapSubCommand::NAK), - "CLEAR" => Some(CapSubCommand::CLEAR), - "END" => Some(CapSubCommand::END), - _ => None, + "LS" => Ok(CapSubCommand::LS), + "LIST" => Ok(CapSubCommand::LIST), + "REQ" => Ok(CapSubCommand::REQ), + "ACK" => Ok(CapSubCommand::ACK), + "NAK" => Ok(CapSubCommand::NAK), + "CLEAR" => Ok(CapSubCommand::CLEAR), + "END" => Ok(CapSubCommand::END), + _ => Err("Failed to parse CAP subcommand."), } } } diff --git a/src/client/data/config.rs b/src/client/data/config.rs index c682fbd..05df701 100644 --- a/src/client/data/config.rs +++ b/src/client/data/config.rs @@ -8,7 +8,7 @@ use std::old_io::{InvalidInput, IoError, IoResult}; use rustc_serialize::json::decode; /// Configuration data. -#[derive(Clone, RustcDecodable, Default, PartialEq, Show)] +#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)] #[stable] pub struct Config { /// A list of the owners of the bot by nickname. diff --git a/src/client/data/message.rs b/src/client/data/message.rs index b79668a..4cb9d67 100644 --- a/src/client/data/message.rs +++ b/src/client/data/message.rs @@ -5,7 +5,7 @@ use std::str::FromStr; /// IRC Message data. #[stable] -#[derive(Clone, PartialEq, Show)] +#[derive(Clone, PartialEq, Debug)] pub struct Message { /// The message prefix (or source) as defined by [RFC 2812](http://tools.ietf.org/html/rfc2812). #[stable] @@ -72,9 +72,10 @@ impl ToMessage for Message { } impl FromStr for Message { - fn from_str(s: &str) -> Option { + type Err = &'static str; + fn from_str(s: &str) -> Result { let mut state = s.clone(); - if s.len() == 0 { return None } + if s.len() == 0 { return Err("Cannot parse an empty string as a message.") } let prefix = if state.starts_with(":") { let prefix = state.find(' ').map(|i| &state[1..i]); state = state.find(' ').map_or("", |i| &state[i+1..]); @@ -94,11 +95,11 @@ impl FromStr for Message { state = state.find(' ').map_or("", |i| &state[i+1..]); cmd } - _ => return None + _ => return Err("Cannot parse a message without a command.") }; if suffix.is_none() { state = &state[..state.len() - 2] } 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)) + Ok(Message::new(prefix, command, if args.len() > 0 { Some(args) } else { None }, suffix)) } } @@ -167,14 +168,14 @@ mod test { args: vec![format!("test")], suffix: Some(format!("Testing!")), }; - assert_eq!("PRIVMSG test :Testing!\r\n".parse(), Some(message)); + assert_eq!("PRIVMSG test :Testing!\r\n".parse(), Ok(message)); let message = Message { prefix: Some(format!("test!test@test")), command: format!("PRIVMSG"), args: vec![format!("test")], suffix: Some(format!("Still testing!")), }; - assert_eq!(":test!test@test PRIVMSG test :Still testing!\r\n".parse(), Some(message)); + assert_eq!(":test!test@test PRIVMSG test :Still testing!\r\n".parse(), Ok(message)); } #[test] diff --git a/src/client/data/response.rs b/src/client/data/response.rs index 5ef88e6..be76b68 100644 --- a/src/client/data/response.rs +++ b/src/client/data/response.rs @@ -7,7 +7,7 @@ use client::data::message::Message; /// List of all server responses as defined in [RFC 2812](http://tools.ietf.org/html/rfc2812). /// All commands are documented with their expected form from the RFC. -#[derive(Copy, Show, PartialEq, FromPrimitive)] +#[derive(Copy, Debug, PartialEq, FromPrimitive)] #[repr(u16)] #[stable] pub enum Response { @@ -435,7 +435,7 @@ impl Response { /// Gets a response from a message. #[stable] pub fn from_message(m: &Message) -> Option { - m.command.parse() + m.command.parse().ok() } /// Determines whether or not this response is an error response. @@ -446,11 +446,12 @@ impl Response { } impl FromStr for Response { - fn from_str(s: &str) -> Option { - if let Some(respcode) = s.parse() { - FromPrimitive::from_u16(respcode) + type Err = &'static str; + fn from_str(s: &str) -> Result { + if let Ok(respcode) = s.parse() { + FromPrimitive::from_u16(respcode).ok_or("Failed to convert response code to u16.") } else { - None + Err("Failed to parse response code.") } } } diff --git a/src/client/data/user.rs b/src/client/data/user.rs index baa95e4..c707065 100644 --- a/src/client/data/user.rs +++ b/src/client/data/user.rs @@ -7,7 +7,7 @@ use std::str::FromStr; /// IRC User data. #[stable] -#[derive(Clone, Show)] +#[derive(Clone, Debug)] pub struct User { /// The user's nickname. /// This is the only detail used in determining the equality of two users. @@ -113,7 +113,7 @@ impl PartialEq for User { /// The user's access level. #[stable] -#[derive(Copy, PartialEq, Clone, Show)] +#[derive(Copy, PartialEq, Clone, Debug)] pub enum AccessLevel { /// The channel owner (~). #[stable] @@ -174,15 +174,16 @@ impl PartialOrd for AccessLevel { } impl FromStr for AccessLevel { - fn from_str(s: &str) -> Option { - if s.len() == 0 { None } else { - Some(match s.char_at(0) { + type Err = &'static str; + fn from_str(s: &str) -> Result { + if s.len() == 0 { Err("No access level in an empty string.") } else { + Ok(match s.char_at(0) { '~' => AccessLevel::Owner, '&' => AccessLevel::Admin, '@' => AccessLevel::Oper, '%' => AccessLevel::HalfOp, '+' => AccessLevel::Voice, - _ => return None, + _ => return Err("Failed to parse access level."), }) } } @@ -206,7 +207,7 @@ impl<'a> Iterator for AccessLevelIterator<'a> { if self.value.len() > 0 { self.value = &self.value[1..]; } - ret + ret.ok() } } @@ -217,13 +218,13 @@ mod test { #[test] fn parse_access_level() { - assert!("member".parse::().is_none()); + assert!("member".parse::().is_err()); assert_eq!("~owner".parse::().unwrap(), Owner); assert_eq!("&admin".parse::().unwrap(), Admin); assert_eq!("@oper".parse::().unwrap(), Oper); assert_eq!("%halfop".parse::().unwrap(), HalfOp); assert_eq!("+voice".parse::().unwrap(), Voice); - assert!("".parse::().is_none()); + assert!("".parse::().is_err()); } #[test] diff --git a/src/client/server/mod.rs b/src/client/server/mod.rs index 893508a..fd9165f 100644 --- a/src/client/server/mod.rs +++ b/src/client/server/mod.rs @@ -280,14 +280,14 @@ impl<'a, T: IrcReader, U: IrcWriter> Iterator for ServerIterator<'a, T, U> { fn next(&mut self) -> Option> { let res = self.get_next_line().and_then(|msg| match msg.parse() { - Some(msg) => { + Ok(msg) => { self.server.handle_message(&msg); Ok(msg) }, - None => Err(IoError { + Err(m) => Err(IoError { kind: IoErrorKind::InvalidInput, desc: "Failed to parse message.", - detail: Some(msg) + detail: Some(format!("{} (Message: {})", m, msg)) }) } );