Migrated to using error_chain.

This commit is contained in:
Aaron Weiss 2017-06-20 14:54:06 -04:00
parent 1ca609331c
commit cc1aa5717e
No known key found for this signature in database
GPG key ID: 0237035D9BF03AE2
11 changed files with 103 additions and 64 deletions

View file

@ -19,6 +19,7 @@ nochanlists = []
[dependencies]
bufstream = "0.1"
bytes = "0.4"
error-chain = "0.10"
encoding = "0.2"
futures = "0.1"
native-tls = "0.1"

View file

@ -1,8 +1,10 @@
//! Thread-safe connections on `IrcStreams`.
use std::io;
use std::io::prelude::*;
use std::io::{Cursor, Result};
use std::io::Cursor;
use std::net::TcpStream;
use std::sync::Mutex;
use error::Result;
use bufstream::BufStream;
use encoding::DecoderTrap;
use encoding::label::encoding_from_whatwg_label;
@ -50,7 +52,7 @@ impl NetConnection {
/// connects to the specified server and returns a reader-writer pair.
fn connect_internal(host: &str, port: u16) -> Result<NetBufStream> {
let socket = try!(TcpStream::connect((host, port)));
let socket = try!(TcpStream::connect((host, port)).into());
Ok(BufStream::new(NetStream::Unsecured(socket)))
}
@ -144,12 +146,11 @@ impl Connection for MockConnection {
}
mod imp {
use std::io::Result;
use std::io::Error;
use std::io::ErrorKind;
use std::io::{Error, ErrorKind};
use std::sync::Mutex;
use encoding::{DecoderTrap, EncoderTrap};
use encoding::label::encoding_from_whatwg_label;
use error::Result;
use client::data::kinds::{IrcRead, IrcWrite};
pub fn send<T: IrcWrite>(writer: &Mutex<T>, msg: &str, encoding: &str) -> Result<()> {
@ -159,7 +160,7 @@ mod imp {
return Err(Error::new(
ErrorKind::InvalidInput,
&format!("Failed to find encoder. ({})", encoding)[..],
))
).into())
}
};
let data = match encoding.encode(msg, EncoderTrap::Replace) {
@ -173,12 +174,12 @@ mod imp {
encoding.name()
)
[..],
))
).into())
}
};
let mut writer = writer.lock().unwrap();
try!(writer.write_all(&data));
writer.flush()
writer.flush().map_err(|e| e.into())
}
pub fn recv<T: IrcRead>(reader: &Mutex<T>, encoding: &str) -> Result<String> {
@ -188,7 +189,7 @@ mod imp {
return Err(Error::new(
ErrorKind::InvalidInput,
&format!("Failed to find decoder. ({})", encoding)[..],
))
).into())
}
};
let mut buf = Vec::new();
@ -209,6 +210,7 @@ mod imp {
[..],
)),
})
.map_err(|e| e.into())
}
}
@ -220,7 +222,7 @@ pub enum NetStream {
}
impl Read for NetStream {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match *self {
NetStream::Unsecured(ref mut stream) => stream.read(buf),
#[cfg(feature = "ssl")]
@ -230,13 +232,13 @@ impl Read for NetStream {
}
impl Write for NetStream {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match *self {
NetStream::Unsecured(ref mut stream) => stream.write(buf),
}
}
fn flush(&mut self) -> Result<()> {
fn flush(&mut self) -> io::Result<()> {
match *self {
NetStream::Unsecured(ref mut stream) => stream.flush(),
}
@ -246,7 +248,7 @@ impl Write for NetStream {
#[cfg(test)]
mod test {
use super::{Connection, MockConnection};
use std::io::Result;
use error::Result;
use client::data::Message;
use client::data::Command::PRIVMSG;

View file

@ -3,10 +3,11 @@ use std::borrow::ToOwned;
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::io::{Error, ErrorKind, Result};
use std::io::{Error, ErrorKind};
use std::net::{SocketAddr, ToSocketAddrs};
use std::path::Path;
use serde_json;
use error::Result;
/// Configuration data.
#[derive(Clone, Deserialize, Serialize, Default, PartialEq, Debug)]
@ -71,7 +72,7 @@ impl Config {
Error::new(
ErrorKind::InvalidInput,
"Failed to decode configuration file.",
)
).into()
})
}
@ -85,7 +86,7 @@ impl Config {
"Failed to encode configuration file.",
)
})).as_bytes(),
)
).map_err(|e| e.into())
}
/// Determines whether or not the nickname provided is the owner of the bot.

View file

@ -3,12 +3,13 @@ use std::borrow::ToOwned;
use std::cell::Cell;
use std::collections::HashMap;
use std::error::Error as StdError;
use std::io::{Error, ErrorKind, Result};
use std::io::{Error, ErrorKind};
use std::path::Path;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{spawn, sleep};
use std::time::Duration as StdDuration;
use error::Result;
use client::conn::{Connection, NetConnection};
use client::data::{Command, Config, Message, Response, User};
use client::data::Command::{JOIN, NICK, NICKSERV, PART, PING, PONG, PRIVMSG, MODE, QUIT};
@ -680,7 +681,7 @@ impl<'a> Iterator for ServerIterator<'a> {
return Some(Err(Error::new(
ErrorKind::InvalidInput,
&format!("Failed to parse message. (Message: {})", msg)[..],
)))
).into()))
}
}
}

View file

@ -1,11 +1,11 @@
//! Utilities and shortcuts for working with IRC servers.
use std::io::Result;
use std::borrow::ToOwned;
use error::Result;
use client::data::{Capability, NegotiationVersion};
use client::data::Command::{AUTHENTICATE, CAP, INVITE, JOIN, KICK, KILL, MODE, NICK, NOTICE};
use client::data::Command::{OPER, PART, PASS, PONG, PRIVMSG, QUIT, SAMODE, SANICK, TOPIC, USER};
use proto::command::CapSubCommand::{END, LS, REQ};
use client::server::Server;
use proto::command::CapSubCommand::{END, LS, REQ};
use time;
/// Extensions for Server capabilities that make it easier to work directly with the protocol.

30
src/error.rs Normal file
View file

@ -0,0 +1,30 @@
//! Errors for `irc` crate using `error_chain`.
#![allow(missing_docs)]
error_chain! {
foreign_links {
Io(::std::io::Error);
Tls(::native_tls::Error);
}
errors {
/// A parsing error for empty strings as messages.
ParseEmpty {
description("Cannot parse an empty string as a message.")
display("Cannot parse an empty string as a message.")
}
/// A parsing error for invalid or missing commands in messages.
InvalidCommand {
description("Message contained a missing or invalid Command.")
display("Message contained a missing or invalid Command.")
}
/// A parsing error for failures in subcommand parsing (e.g. CAP and metadata).
SubCommandParsingFailed {
description("Failed to parse an IRC subcommand.")
display("Failed to parse an IRC subcommand.")
}
}
}

View file

@ -4,7 +4,10 @@
extern crate bufstream;
extern crate bytes;
#[macro_use]
extern crate error_chain;
extern crate encoding;
#[macro_use]
extern crate futures;
extern crate native_tls;
extern crate serde;
@ -18,5 +21,6 @@ extern crate tokio_service;
extern crate tokio_tls;
pub mod client;
pub mod error;
pub mod proto;
pub mod server;

View file

@ -1,7 +1,6 @@
//! Enumeration of all available client commands.
use std::io::Result;
use std::result::Result as StdResult;
use std::str::FromStr;
use error;
use client::data::Response;
/// List of all client commands as defined in [RFC 2812](http://tools.ietf.org/html/rfc2812). This
@ -434,7 +433,7 @@ impl<'a> From<&'a Command> for String {
impl Command {
/// Constructs a new Command.
pub fn new(cmd: &str, args: Vec<&str>, suffix: Option<&str>) -> Result<Command> {
pub fn new(cmd: &str, args: Vec<&str>, suffix: Option<&str>) -> error::Result<Command> {
Ok(if let "PASS" = cmd {
match suffix {
Some(suffix) => {
@ -1659,8 +1658,8 @@ impl CapSubCommand {
}
impl FromStr for CapSubCommand {
type Err = &'static str;
fn from_str(s: &str) -> StdResult<CapSubCommand, &'static str> {
type Err = error::Error;
fn from_str(s: &str) -> error::Result<CapSubCommand> {
match s {
"LS" => Ok(CapSubCommand::LS),
"LIST" => Ok(CapSubCommand::LIST),
@ -1670,7 +1669,7 @@ impl FromStr for CapSubCommand {
"END" => Ok(CapSubCommand::END),
"NEW" => Ok(CapSubCommand::NEW),
"DEL" => Ok(CapSubCommand::DEL),
_ => Err("Failed to parse CAP subcommand."),
_ => Err(error::ErrorKind::SubCommandParsingFailed.into()),
}
}
}
@ -1702,14 +1701,14 @@ impl MetadataSubCommand {
}
impl FromStr for MetadataSubCommand {
type Err = &'static str;
fn from_str(s: &str) -> StdResult<MetadataSubCommand, &'static str> {
type Err = error::Error;
fn from_str(s: &str) -> error::Result<MetadataSubCommand> {
match s {
"GET" => Ok(MetadataSubCommand::GET),
"LIST" => Ok(MetadataSubCommand::LIST),
"SET" => Ok(MetadataSubCommand::SET),
"CLEAR" => Ok(MetadataSubCommand::CLEAR),
_ => Err("Failed to parse METADATA subcommand."),
_ => Err(error::ErrorKind::SubCommandParsingFailed.into()),
}
}
}
@ -1737,8 +1736,8 @@ impl BatchSubCommand {
}
impl FromStr for BatchSubCommand {
type Err = &'static str;
fn from_str(s: &str) -> StdResult<BatchSubCommand, &'static str> {
type Err = error::Error;
fn from_str(s: &str) -> error::Result<BatchSubCommand> {
match s {
"NETSPLIT" => Ok(BatchSubCommand::NETSPLIT),
"NETJOIN" => Ok(BatchSubCommand::NETJOIN),

View file

@ -1,6 +1,6 @@
//! Implementation of IRC codec for Tokio.
use std::io;
use error;
use bytes::BytesMut;
use tokio_io::codec::{Decoder, Encoder};
use proto::line::LineCodec;
@ -13,30 +13,28 @@ pub struct IrcCodec {
impl IrcCodec {
/// Creates a new instance of IrcCodec wrapping a LineCodec with the specific encoding.
pub fn new(label: &str) -> io::Result<IrcCodec> {
pub fn new(label: &str) -> error::Result<IrcCodec> {
LineCodec::new(label).map(|codec| IrcCodec { inner: codec })
}
}
impl Decoder for IrcCodec {
type Item = Message;
type Error = io::Error;
type Error = error::Error;
fn decode(&mut self, src: &mut BytesMut) -> io::Result<Option<Message>> {
fn decode(&mut self, src: &mut BytesMut) -> error::Result<Option<Message>> {
self.inner.decode(src).and_then(|res| res.map_or(Ok(None), |msg| {
msg.parse::<Message>().map(|msg| Some(msg)).map_err(|err| {
io::Error::new(io::ErrorKind::InvalidInput, err)
})
msg.parse::<Message>().map(|msg| Some(msg))
}))
}
}
impl Encoder for IrcCodec {
type Item = Message;
type Error = io::Error;
type Error = error::Error;
fn encode(&mut self, msg: Message, dst: &mut BytesMut) -> io::Result<()> {
fn encode(&mut self, msg: Message, dst: &mut BytesMut) -> error::Result<()> {
self.inner.encode(msg.to_string(), dst)
}
}

View file

@ -1,6 +1,7 @@
//! Implementation of line-delimiting codec for Tokio.
use std::io;
use error;
use bytes::{BufMut, BytesMut};
use encoding::{DecoderTrap, EncoderTrap, EncodingRef};
use encoding::label::encoding_from_whatwg_label;
@ -13,21 +14,21 @@ pub struct LineCodec {
impl LineCodec {
/// Creates a new instance of LineCodec from the specified encoding.
pub fn new(label: &str) -> io::Result<LineCodec> {
pub fn new(label: &str) -> error::Result<LineCodec> {
encoding_from_whatwg_label(label).map(|enc| LineCodec { encoding: enc }).ok_or(
io::Error::new(
io::ErrorKind::InvalidInput,
&format!("Attempted to use unknown codec {}.", label)[..]
)
).into()
)
}
}
impl Decoder for LineCodec {
type Item = String;
type Error = io::Error;
type Error = error::Error;
fn decode(&mut self, src: &mut BytesMut) -> io::Result<Option<String>> {
fn decode(&mut self, src: &mut BytesMut) -> error::Result<Option<String>> {
if let Some(n) = src.as_ref().iter().position(|b| *b == b'\n') {
// Remove the next frame from the buffer.
let line = src.split_to(n + 1);
@ -41,7 +42,7 @@ impl Decoder for LineCodec {
Err(data) => Err(io::Error::new(
io::ErrorKind::InvalidInput,
&format!("Failed to decode {} as {}.", data, self.encoding.name())[..]
))
).into())
}
} else {
Ok(None)
@ -51,19 +52,19 @@ impl Decoder for LineCodec {
impl Encoder for LineCodec {
type Item = String;
type Error = io::Error;
type Error = error::Error;
fn encode(&mut self, msg: String, dst: &mut BytesMut) -> io::Result<()> {
fn encode(&mut self, msg: String, dst: &mut BytesMut) -> error::Result<()> {
// Encode the message using the codec's encoding.
let data = try!(self.encoding.encode(&msg, EncoderTrap::Replace).map_err(|data| {
let data: error::Result<Vec<u8>> = self.encoding.encode(&msg, EncoderTrap::Replace).map_err(|data| {
io::Error::new(
io::ErrorKind::InvalidInput,
&format!("Failed to encode {} as {}.", data, self.encoding.name())[..]
)
}));
).into()
});
// Write the encoded message to the output buffer.
dst.put(&data);
dst.put(&data?);
Ok(())
}

View file

@ -1,8 +1,9 @@
//! Messages to and from the server.
use std::borrow::ToOwned;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::io::Result as IoResult;
use std::str::FromStr;
use error;
use error::{Error, ErrorKind};
use client::data::Command;
/// IRC Message data.
@ -23,7 +24,7 @@ impl Message {
command: &str,
args: Vec<&str>,
suffix: Option<&str>,
) -> IoResult<Message> {
) -> error::Result<Message> {
Message::with_tags(None, prefix, command, args, suffix)
}
@ -34,7 +35,7 @@ impl Message {
command: &str,
args: Vec<&str>,
suffix: Option<&str>,
) -> IoResult<Message> {
) -> error::Result<Message> {
Ok(Message {
tags: tags,
prefix: prefix.map(|s| s.to_owned()),
@ -85,11 +86,12 @@ impl From<Command> for Message {
}
impl FromStr for Message {
type Err = &'static str;
fn from_str(s: &str) -> Result<Message, &'static str> {
type Err = Error;
fn from_str(s: &str) -> Result<Message, Self::Err> {
let mut state = s;
if s.is_empty() {
return Err("Cannot parse an empty string as a message.");
return Err(ErrorKind::ParseEmpty.into());
}
let tags = if state.starts_with('@') {
let tags = state.find(' ').map(|i| &state[1..i]);
@ -126,14 +128,14 @@ impl FromStr for Message {
state = state.find(' ').map_or("", |i| &state[i + 1..]);
cmd
}
_ => return Err("Cannot parse a message without a command."),
_ => return Err(ErrorKind::InvalidCommand.into()),
};
if suffix.is_none() {
state = &state[..state.len() - 2]
}
let args: Vec<_> = state.splitn(14, ' ').filter(|s| !s.is_empty()).collect();
Message::with_tags(tags, prefix, command, args, suffix)
.map_err(|_| "Invalid input for Command.")
.map_err(|_| ErrorKind::InvalidCommand.into())
}
}
@ -256,15 +258,15 @@ mod test {
prefix: None,
command: PRIVMSG(format!("test"), format!("Testing!")),
};
assert_eq!("PRIVMSG test :Testing!\r\n".parse(), Ok(message));
assert_eq!("PRIVMSG test :Testing!\r\n".parse::<Message>().unwrap(), message);
let message = Message {
tags: None,
prefix: Some(format!("test!test@test")),
command: PRIVMSG(format!("test"), format!("Still testing!")),
};
assert_eq!(
":test!test@test PRIVMSG test :Still testing!\r\n".parse(),
Ok(message)
":test!test@test PRIVMSG test :Still testing!\r\n".parse::<Message>().unwrap(),
message
);
let message = Message {
tags: Some(vec![
@ -278,8 +280,8 @@ mod test {
assert_eq!(
"@aaa=bbb;ccc;example.com/ddd=eee :test!test@test PRIVMSG test :Testing with \
tags!\r\n"
.parse(),
Ok(message)
.parse::<Message>().unwrap(),
message
)
}