Migrated to using error_chain.
This commit is contained in:
parent
1ca609331c
commit
cc1aa5717e
11 changed files with 103 additions and 64 deletions
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
30
src/error.rs
Normal 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.")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue