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]
|
[dependencies]
|
||||||
bufstream = "0.1"
|
bufstream = "0.1"
|
||||||
bytes = "0.4"
|
bytes = "0.4"
|
||||||
|
error-chain = "0.10"
|
||||||
encoding = "0.2"
|
encoding = "0.2"
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
native-tls = "0.1"
|
native-tls = "0.1"
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
//! Thread-safe connections on `IrcStreams`.
|
//! Thread-safe connections on `IrcStreams`.
|
||||||
|
use std::io;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::{Cursor, Result};
|
use std::io::Cursor;
|
||||||
use std::net::TcpStream;
|
use std::net::TcpStream;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use error::Result;
|
||||||
use bufstream::BufStream;
|
use bufstream::BufStream;
|
||||||
use encoding::DecoderTrap;
|
use encoding::DecoderTrap;
|
||||||
use encoding::label::encoding_from_whatwg_label;
|
use encoding::label::encoding_from_whatwg_label;
|
||||||
|
@ -50,7 +52,7 @@ impl NetConnection {
|
||||||
|
|
||||||
/// connects to the specified server and returns a reader-writer pair.
|
/// connects to the specified server and returns a reader-writer pair.
|
||||||
fn connect_internal(host: &str, port: u16) -> Result<NetBufStream> {
|
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)))
|
Ok(BufStream::new(NetStream::Unsecured(socket)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,12 +146,11 @@ impl Connection for MockConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use std::io::Result;
|
use std::io::{Error, ErrorKind};
|
||||||
use std::io::Error;
|
|
||||||
use std::io::ErrorKind;
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use encoding::{DecoderTrap, EncoderTrap};
|
use encoding::{DecoderTrap, EncoderTrap};
|
||||||
use encoding::label::encoding_from_whatwg_label;
|
use encoding::label::encoding_from_whatwg_label;
|
||||||
|
use error::Result;
|
||||||
use client::data::kinds::{IrcRead, IrcWrite};
|
use client::data::kinds::{IrcRead, IrcWrite};
|
||||||
|
|
||||||
pub fn send<T: IrcWrite>(writer: &Mutex<T>, msg: &str, encoding: &str) -> Result<()> {
|
pub fn send<T: IrcWrite>(writer: &Mutex<T>, msg: &str, encoding: &str) -> Result<()> {
|
||||||
|
@ -159,7 +160,7 @@ mod imp {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
ErrorKind::InvalidInput,
|
ErrorKind::InvalidInput,
|
||||||
&format!("Failed to find encoder. ({})", encoding)[..],
|
&format!("Failed to find encoder. ({})", encoding)[..],
|
||||||
))
|
).into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let data = match encoding.encode(msg, EncoderTrap::Replace) {
|
let data = match encoding.encode(msg, EncoderTrap::Replace) {
|
||||||
|
@ -173,12 +174,12 @@ mod imp {
|
||||||
encoding.name()
|
encoding.name()
|
||||||
)
|
)
|
||||||
[..],
|
[..],
|
||||||
))
|
).into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut writer = writer.lock().unwrap();
|
let mut writer = writer.lock().unwrap();
|
||||||
try!(writer.write_all(&data));
|
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> {
|
pub fn recv<T: IrcRead>(reader: &Mutex<T>, encoding: &str) -> Result<String> {
|
||||||
|
@ -188,7 +189,7 @@ mod imp {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
ErrorKind::InvalidInput,
|
ErrorKind::InvalidInput,
|
||||||
&format!("Failed to find decoder. ({})", encoding)[..],
|
&format!("Failed to find decoder. ({})", encoding)[..],
|
||||||
))
|
).into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
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 {
|
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 {
|
match *self {
|
||||||
NetStream::Unsecured(ref mut stream) => stream.read(buf),
|
NetStream::Unsecured(ref mut stream) => stream.read(buf),
|
||||||
#[cfg(feature = "ssl")]
|
#[cfg(feature = "ssl")]
|
||||||
|
@ -230,13 +232,13 @@ impl Read for NetStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write 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 {
|
match *self {
|
||||||
NetStream::Unsecured(ref mut stream) => stream.write(buf),
|
NetStream::Unsecured(ref mut stream) => stream.write(buf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> Result<()> {
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
match *self {
|
match *self {
|
||||||
NetStream::Unsecured(ref mut stream) => stream.flush(),
|
NetStream::Unsecured(ref mut stream) => stream.flush(),
|
||||||
}
|
}
|
||||||
|
@ -246,7 +248,7 @@ impl Write for NetStream {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{Connection, MockConnection};
|
use super::{Connection, MockConnection};
|
||||||
use std::io::Result;
|
use error::Result;
|
||||||
use client::data::Message;
|
use client::data::Message;
|
||||||
use client::data::Command::PRIVMSG;
|
use client::data::Command::PRIVMSG;
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,11 @@ use std::borrow::ToOwned;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::{Error, ErrorKind, Result};
|
use std::io::{Error, ErrorKind};
|
||||||
use std::net::{SocketAddr, ToSocketAddrs};
|
use std::net::{SocketAddr, ToSocketAddrs};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
use error::Result;
|
||||||
|
|
||||||
/// Configuration data.
|
/// Configuration data.
|
||||||
#[derive(Clone, Deserialize, Serialize, Default, PartialEq, Debug)]
|
#[derive(Clone, Deserialize, Serialize, Default, PartialEq, Debug)]
|
||||||
|
@ -71,7 +72,7 @@ impl Config {
|
||||||
Error::new(
|
Error::new(
|
||||||
ErrorKind::InvalidInput,
|
ErrorKind::InvalidInput,
|
||||||
"Failed to decode configuration file.",
|
"Failed to decode configuration file.",
|
||||||
)
|
).into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +86,7 @@ impl Config {
|
||||||
"Failed to encode configuration file.",
|
"Failed to encode configuration file.",
|
||||||
)
|
)
|
||||||
})).as_bytes(),
|
})).as_bytes(),
|
||||||
)
|
).map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether or not the nickname provided is the owner of the bot.
|
/// 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::cell::Cell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::io::{Error, ErrorKind, Result};
|
use std::io::{Error, ErrorKind};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::thread::{spawn, sleep};
|
use std::thread::{spawn, sleep};
|
||||||
use std::time::Duration as StdDuration;
|
use std::time::Duration as StdDuration;
|
||||||
|
use error::Result;
|
||||||
use client::conn::{Connection, NetConnection};
|
use client::conn::{Connection, NetConnection};
|
||||||
use client::data::{Command, Config, Message, Response, User};
|
use client::data::{Command, Config, Message, Response, User};
|
||||||
use client::data::Command::{JOIN, NICK, NICKSERV, PART, PING, PONG, PRIVMSG, MODE, QUIT};
|
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(
|
return Some(Err(Error::new(
|
||||||
ErrorKind::InvalidInput,
|
ErrorKind::InvalidInput,
|
||||||
&format!("Failed to parse message. (Message: {})", msg)[..],
|
&format!("Failed to parse message. (Message: {})", msg)[..],
|
||||||
)))
|
).into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
//! Utilities and shortcuts for working with IRC servers.
|
//! Utilities and shortcuts for working with IRC servers.
|
||||||
use std::io::Result;
|
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
|
use error::Result;
|
||||||
use client::data::{Capability, NegotiationVersion};
|
use client::data::{Capability, NegotiationVersion};
|
||||||
use client::data::Command::{AUTHENTICATE, CAP, INVITE, JOIN, KICK, KILL, MODE, NICK, NOTICE};
|
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 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 client::server::Server;
|
||||||
|
use proto::command::CapSubCommand::{END, LS, REQ};
|
||||||
use time;
|
use time;
|
||||||
|
|
||||||
/// Extensions for Server capabilities that make it easier to work directly with the protocol.
|
/// 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 bufstream;
|
||||||
extern crate bytes;
|
extern crate bytes;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate error_chain;
|
||||||
extern crate encoding;
|
extern crate encoding;
|
||||||
|
#[macro_use]
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
extern crate native_tls;
|
extern crate native_tls;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
@ -18,5 +21,6 @@ extern crate tokio_service;
|
||||||
extern crate tokio_tls;
|
extern crate tokio_tls;
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
pub mod error;
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
//! Enumeration of all available client commands.
|
//! Enumeration of all available client commands.
|
||||||
use std::io::Result;
|
|
||||||
use std::result::Result as StdResult;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use error;
|
||||||
use client::data::Response;
|
use client::data::Response;
|
||||||
|
|
||||||
/// List of all client commands as defined in [RFC 2812](http://tools.ietf.org/html/rfc2812). This
|
/// 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 {
|
impl Command {
|
||||||
/// Constructs a new 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 {
|
Ok(if let "PASS" = cmd {
|
||||||
match suffix {
|
match suffix {
|
||||||
Some(suffix) => {
|
Some(suffix) => {
|
||||||
|
@ -1659,8 +1658,8 @@ impl CapSubCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for CapSubCommand {
|
impl FromStr for CapSubCommand {
|
||||||
type Err = &'static str;
|
type Err = error::Error;
|
||||||
fn from_str(s: &str) -> StdResult<CapSubCommand, &'static str> {
|
fn from_str(s: &str) -> error::Result<CapSubCommand> {
|
||||||
match s {
|
match s {
|
||||||
"LS" => Ok(CapSubCommand::LS),
|
"LS" => Ok(CapSubCommand::LS),
|
||||||
"LIST" => Ok(CapSubCommand::LIST),
|
"LIST" => Ok(CapSubCommand::LIST),
|
||||||
|
@ -1670,7 +1669,7 @@ impl FromStr for CapSubCommand {
|
||||||
"END" => Ok(CapSubCommand::END),
|
"END" => Ok(CapSubCommand::END),
|
||||||
"NEW" => Ok(CapSubCommand::NEW),
|
"NEW" => Ok(CapSubCommand::NEW),
|
||||||
"DEL" => Ok(CapSubCommand::DEL),
|
"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 {
|
impl FromStr for MetadataSubCommand {
|
||||||
type Err = &'static str;
|
type Err = error::Error;
|
||||||
fn from_str(s: &str) -> StdResult<MetadataSubCommand, &'static str> {
|
fn from_str(s: &str) -> error::Result<MetadataSubCommand> {
|
||||||
match s {
|
match s {
|
||||||
"GET" => Ok(MetadataSubCommand::GET),
|
"GET" => Ok(MetadataSubCommand::GET),
|
||||||
"LIST" => Ok(MetadataSubCommand::LIST),
|
"LIST" => Ok(MetadataSubCommand::LIST),
|
||||||
"SET" => Ok(MetadataSubCommand::SET),
|
"SET" => Ok(MetadataSubCommand::SET),
|
||||||
"CLEAR" => Ok(MetadataSubCommand::CLEAR),
|
"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 {
|
impl FromStr for BatchSubCommand {
|
||||||
type Err = &'static str;
|
type Err = error::Error;
|
||||||
fn from_str(s: &str) -> StdResult<BatchSubCommand, &'static str> {
|
fn from_str(s: &str) -> error::Result<BatchSubCommand> {
|
||||||
match s {
|
match s {
|
||||||
"NETSPLIT" => Ok(BatchSubCommand::NETSPLIT),
|
"NETSPLIT" => Ok(BatchSubCommand::NETSPLIT),
|
||||||
"NETJOIN" => Ok(BatchSubCommand::NETJOIN),
|
"NETJOIN" => Ok(BatchSubCommand::NETJOIN),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Implementation of IRC codec for Tokio.
|
//! Implementation of IRC codec for Tokio.
|
||||||
|
|
||||||
use std::io;
|
use error;
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use tokio_io::codec::{Decoder, Encoder};
|
use tokio_io::codec::{Decoder, Encoder};
|
||||||
use proto::line::LineCodec;
|
use proto::line::LineCodec;
|
||||||
|
@ -13,30 +13,28 @@ pub struct IrcCodec {
|
||||||
|
|
||||||
impl IrcCodec {
|
impl IrcCodec {
|
||||||
/// Creates a new instance of IrcCodec wrapping a LineCodec with the specific encoding.
|
/// 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 })
|
LineCodec::new(label).map(|codec| IrcCodec { inner: codec })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoder for IrcCodec {
|
impl Decoder for IrcCodec {
|
||||||
type Item = Message;
|
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| {
|
self.inner.decode(src).and_then(|res| res.map_or(Ok(None), |msg| {
|
||||||
msg.parse::<Message>().map(|msg| Some(msg)).map_err(|err| {
|
msg.parse::<Message>().map(|msg| Some(msg))
|
||||||
io::Error::new(io::ErrorKind::InvalidInput, err)
|
|
||||||
})
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encoder for IrcCodec {
|
impl Encoder for IrcCodec {
|
||||||
type Item = Message;
|
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)
|
self.inner.encode(msg.to_string(), dst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//! Implementation of line-delimiting codec for Tokio.
|
//! Implementation of line-delimiting codec for Tokio.
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use error;
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
use encoding::{DecoderTrap, EncoderTrap, EncodingRef};
|
use encoding::{DecoderTrap, EncoderTrap, EncodingRef};
|
||||||
use encoding::label::encoding_from_whatwg_label;
|
use encoding::label::encoding_from_whatwg_label;
|
||||||
|
@ -13,21 +14,21 @@ pub struct LineCodec {
|
||||||
|
|
||||||
impl LineCodec {
|
impl LineCodec {
|
||||||
/// Creates a new instance of LineCodec from the specified encoding.
|
/// 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(
|
encoding_from_whatwg_label(label).map(|enc| LineCodec { encoding: enc }).ok_or(
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::InvalidInput,
|
io::ErrorKind::InvalidInput,
|
||||||
&format!("Attempted to use unknown codec {}.", label)[..]
|
&format!("Attempted to use unknown codec {}.", label)[..]
|
||||||
)
|
).into()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decoder for LineCodec {
|
impl Decoder for LineCodec {
|
||||||
type Item = String;
|
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') {
|
if let Some(n) = src.as_ref().iter().position(|b| *b == b'\n') {
|
||||||
// Remove the next frame from the buffer.
|
// Remove the next frame from the buffer.
|
||||||
let line = src.split_to(n + 1);
|
let line = src.split_to(n + 1);
|
||||||
|
@ -41,7 +42,7 @@ impl Decoder for LineCodec {
|
||||||
Err(data) => Err(io::Error::new(
|
Err(data) => Err(io::Error::new(
|
||||||
io::ErrorKind::InvalidInput,
|
io::ErrorKind::InvalidInput,
|
||||||
&format!("Failed to decode {} as {}.", data, self.encoding.name())[..]
|
&format!("Failed to decode {} as {}.", data, self.encoding.name())[..]
|
||||||
))
|
).into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -51,19 +52,19 @@ impl Decoder for LineCodec {
|
||||||
|
|
||||||
impl Encoder for LineCodec {
|
impl Encoder for LineCodec {
|
||||||
type Item = String;
|
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.
|
// 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::Error::new(
|
||||||
io::ErrorKind::InvalidInput,
|
io::ErrorKind::InvalidInput,
|
||||||
&format!("Failed to encode {} as {}.", data, self.encoding.name())[..]
|
&format!("Failed to encode {} as {}.", data, self.encoding.name())[..]
|
||||||
)
|
).into()
|
||||||
}));
|
});
|
||||||
|
|
||||||
// Write the encoded message to the output buffer.
|
// Write the encoded message to the output buffer.
|
||||||
dst.put(&data);
|
dst.put(&data?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
//! Messages to and from the server.
|
//! Messages to and from the server.
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||||
use std::io::Result as IoResult;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use error;
|
||||||
|
use error::{Error, ErrorKind};
|
||||||
use client::data::Command;
|
use client::data::Command;
|
||||||
|
|
||||||
/// IRC Message data.
|
/// IRC Message data.
|
||||||
|
@ -23,7 +24,7 @@ impl Message {
|
||||||
command: &str,
|
command: &str,
|
||||||
args: Vec<&str>,
|
args: Vec<&str>,
|
||||||
suffix: Option<&str>,
|
suffix: Option<&str>,
|
||||||
) -> IoResult<Message> {
|
) -> error::Result<Message> {
|
||||||
Message::with_tags(None, prefix, command, args, suffix)
|
Message::with_tags(None, prefix, command, args, suffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ impl Message {
|
||||||
command: &str,
|
command: &str,
|
||||||
args: Vec<&str>,
|
args: Vec<&str>,
|
||||||
suffix: Option<&str>,
|
suffix: Option<&str>,
|
||||||
) -> IoResult<Message> {
|
) -> error::Result<Message> {
|
||||||
Ok(Message {
|
Ok(Message {
|
||||||
tags: tags,
|
tags: tags,
|
||||||
prefix: prefix.map(|s| s.to_owned()),
|
prefix: prefix.map(|s| s.to_owned()),
|
||||||
|
@ -85,11 +86,12 @@ impl From<Command> for Message {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Message {
|
impl FromStr for Message {
|
||||||
type Err = &'static str;
|
type Err = Error;
|
||||||
fn from_str(s: &str) -> Result<Message, &'static str> {
|
|
||||||
|
fn from_str(s: &str) -> Result<Message, Self::Err> {
|
||||||
let mut state = s;
|
let mut state = s;
|
||||||
if s.is_empty() {
|
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 = if state.starts_with('@') {
|
||||||
let tags = state.find(' ').map(|i| &state[1..i]);
|
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..]);
|
state = state.find(' ').map_or("", |i| &state[i + 1..]);
|
||||||
cmd
|
cmd
|
||||||
}
|
}
|
||||||
_ => return Err("Cannot parse a message without a command."),
|
_ => return Err(ErrorKind::InvalidCommand.into()),
|
||||||
};
|
};
|
||||||
if suffix.is_none() {
|
if suffix.is_none() {
|
||||||
state = &state[..state.len() - 2]
|
state = &state[..state.len() - 2]
|
||||||
}
|
}
|
||||||
let args: Vec<_> = state.splitn(14, ' ').filter(|s| !s.is_empty()).collect();
|
let args: Vec<_> = state.splitn(14, ' ').filter(|s| !s.is_empty()).collect();
|
||||||
Message::with_tags(tags, prefix, command, args, suffix)
|
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,
|
prefix: None,
|
||||||
command: PRIVMSG(format!("test"), format!("Testing!")),
|
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 {
|
let message = Message {
|
||||||
tags: None,
|
tags: None,
|
||||||
prefix: Some(format!("test!test@test")),
|
prefix: Some(format!("test!test@test")),
|
||||||
command: PRIVMSG(format!("test"), format!("Still testing!")),
|
command: PRIVMSG(format!("test"), format!("Still testing!")),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
":test!test@test PRIVMSG test :Still testing!\r\n".parse(),
|
":test!test@test PRIVMSG test :Still testing!\r\n".parse::<Message>().unwrap(),
|
||||||
Ok(message)
|
message
|
||||||
);
|
);
|
||||||
let message = Message {
|
let message = Message {
|
||||||
tags: Some(vec![
|
tags: Some(vec![
|
||||||
|
@ -278,8 +280,8 @@ mod test {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"@aaa=bbb;ccc;example.com/ddd=eee :test!test@test PRIVMSG test :Testing with \
|
"@aaa=bbb;ccc;example.com/ddd=eee :test!test@test PRIVMSG test :Testing with \
|
||||||
tags!\r\n"
|
tags!\r\n"
|
||||||
.parse(),
|
.parse::<Message>().unwrap(),
|
||||||
Ok(message)
|
message
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue