Implemented a major redesign to simplify API and eliminate

overly-complicated generics usage.
This commit is contained in:
Aaron Weiss 2016-02-10 00:15:08 -05:00
parent bacf6d019e
commit 3314c93c3e
5 changed files with 214 additions and 167 deletions

View file

@ -1,35 +1,71 @@
//! Thread-safe connections on IrcStreams.
#[cfg(feature = "ssl")] use std::error::Error as StdError;
use std::io::prelude::*;
use std::io::{BufReader, BufWriter, Cursor, Empty, Result, Sink};
use std::io::{BufReader, BufWriter, Cursor, Result};
use std::io::Error;
use std::io::ErrorKind;
use std::net::TcpStream;
#[cfg(feature = "ssl")] use std::result::Result as StdResult;
use std::sync::{Mutex, MutexGuard};
use std::sync::Mutex;
#[cfg(feature = "encode")] use encoding::{DecoderTrap, EncoderTrap, Encoding};
#[cfg(feature = "encode")] use encoding::label::encoding_from_whatwg_label;
use client::data::Message;
use client::data::kinds::{IrcRead, IrcWrite};
#[cfg(feature = "ssl")] use openssl::ssl::{SslContext, SslMethod, SslStream};
#[cfg(feature = "ssl")] use openssl::ssl::error::SslError;
/// A thread-safe connection.
pub struct Connection<T: IrcRead, U: IrcWrite> {
reader: Mutex<T>,
writer: Mutex<U>,
/// A connection.
pub trait Connection {
/// Sends a message over this connection.
#[cfg(feature = "encode")]
fn send(&self, msg: &str, encoding: &str) -> Result<()>;
/// Sends a message over this connection.
#[cfg(not(feature = "encode"))]
fn send(&self, msg: &str) -> Result<()>;
/// Receives a single line from this connection.
#[cfg(feature = "encoding")]
fn recv(&self, encoding: &str) -> Result<String>;
/// Receives a single line from this connection.
#[cfg(not(feature = "encoding"))]
fn recv(&self) -> Result<String>;
/// Gets the full record of all sent messages if the Connection records this.
/// This is intended for use in writing tests.
fn written(&self) -> Option<String>;
/// Re-establishes this connection, disconnecting from the existing case if necessary.
fn reconnect(&self) -> Result<()>;
}
/// A Connection over a buffered NetStream.
pub type NetConnection = Connection<BufReader<NetStream>, BufWriter<NetStream>>;
/// An internal type
type NetReadWritePair = (BufReader<NetStream>, BufWriter<NetStream>);
impl Connection<BufReader<NetStream>, BufWriter<NetStream>> {
/// Useful internal type definitions.
type NetReader = BufReader<NetStream>;
type NetWriter = BufWriter<NetStream>;
type NetReadWritePair = (NetReader, NetWriter);
/// A thread-safe connection over a buffered NetStream.
pub struct NetConnection {
host: Mutex<String>,
port: Mutex<u16>,
reader: Mutex<NetReader>,
writer: Mutex<NetWriter>,
}
impl NetConnection {
fn new(host: &str, port: u16, reader: NetReader, writer: NetWriter) -> NetConnection {
NetConnection {
host: Mutex::new(host.to_owned()),
port: Mutex::new(port),
reader: Mutex::new(reader),
writer: Mutex::new(writer),
}
}
/// Creates a thread-safe TCP connection to the specified server.
pub fn connect(host: &str, port: u16) -> Result<NetConnection> {
let (reader, writer) = try!(Connection::connect_internal(host, port));
Ok(Connection::new(reader, writer))
let (reader, writer) = try!(NetConnection::connect_internal(host, port));
Ok(NetConnection::new(host, port, reader, writer))
}
/// connects to the specified server and returns a reader-writer pair.
@ -42,8 +78,8 @@ impl Connection<BufReader<NetStream>, BufWriter<NetStream>> {
/// Creates a thread-safe TCP connection to the specified server over SSL.
/// If the library is compiled without SSL support, this method panics.
pub fn connect_ssl(host: &str, port: u16) -> Result<NetConnection> {
let (reader, writer) = try!(Connection::connect_ssl_internal(host, port));
Ok(Connection::new(reader, writer))
let (reader, writer) = try!(NetConnection::connect_ssl_internal(host, port));
Ok(NetConnection::new(host, port, reader, writer))
}
/// Connects over SSL to the specified server and returns a reader-writer pair.
@ -61,46 +97,29 @@ impl Connection<BufReader<NetStream>, BufWriter<NetStream>> {
fn connect_ssl_internal(host: &str, port: u16) -> Result<NetReadWritePair> {
panic!("Cannot connect to {}:{} over SSL without compiling with SSL support.", host, port)
}
/*
FIXME: removed until set_keepalive is stabilized.
/// Sets the keepalive for the network stream.
#[unstable = "Rust IO has not stabilized."]
pub fn set_keepalive(&self, delay_in_seconds: Option<u32>) -> Result<()> {
self.mod_stream(|tcp| tcp.set_keepalive(delay_in_seconds))
}
/// Modifies the internal TcpStream using a function.
fn mod_stream<F>(&self, f: F) -> Result<()> where F: FnOnce(&mut TcpStream) -> Result<()> {
match self.reader.lock().unwrap().get_mut() {
&mut NetStream::UnsecuredTcpStream(ref mut tcp) => f(tcp),
#[cfg(feature = "ssl")]
&mut NetStream::SslTcpStream(ref mut ssl) => f(ssl.get_mut()),
}
}
*/
}
impl<T: IrcRead, U: IrcWrite> Connection<T, U> {
/// Creates a new connection from an IrcReader and an IrcWriter.
pub fn new(reader: T, writer: U) -> Connection<T, U> {
Connection {
reader: Mutex::new(reader),
writer: Mutex::new(writer),
}
/// Converts a Result<T, SslError> into an Result<T>.
#[cfg(feature = "ssl")]
fn ssl_to_io<T>(res: StdResult<T, SslError>) -> Result<T> {
match res {
Ok(x) => Ok(x),
Err(e) => Err(Error::new(ErrorKind::Other,
&format!("An SSL error occurred. ({})", e.description())[..]
)),
}
}
/// Sends a Message over this connection.
impl Connection for NetConnection {
#[cfg(feature = "encode")]
pub fn send<M: Into<Message>>(&self, to_msg: M, encoding: &str) -> Result<()> {
fn send(&self, msg: &str, encoding: &str) -> Result<()> {
let encoding = match encoding_from_whatwg_label(encoding) {
Some(enc) => enc,
None => return Err(Error::new(
ErrorKind::InvalidInput, &format!("Failed to find encoder. ({})", encoding)[..]
))
};
let msg: Message = to_msg.into();
let data = match encoding.encode(&msg.into_string(), EncoderTrap::Replace) {
let data = match encoding.encode(msg, EncoderTrap::Replace) {
Ok(data) => data,
Err(data) => return Err(Error::new(ErrorKind::InvalidInput,
&format!("Failed to encode {} as {}.", data, encoding.name())[..]
@ -111,18 +130,15 @@ impl<T: IrcRead, U: IrcWrite> Connection<T, U> {
writer.flush()
}
/// Sends a message over this connection.
#[cfg(not(feature = "encode"))]
pub fn send<M: Into<Message>>(&self, to_msg: M) -> Result<()> {
fn send(&self, msg: &str) -> Result<()> {
let mut writer = self.writer.lock().unwrap();
let msg: Message = to_msg.into();
try!(writer.write_all(&msg.into_string().as_bytes()));
try!(writer.write_all(msg.as_bytes()));
writer.flush()
}
/// Receives a single line from this connection.
#[cfg(feature = "encoding")]
pub fn recv(&self, encoding: &str) -> Result<String> {
fn recv(&self, encoding: &str) -> Result<String> {
let encoding = match encoding_from_whatwg_label(encoding) {
Some(enc) => enc,
None => return Err(Error::new(
@ -141,9 +157,8 @@ impl<T: IrcRead, U: IrcWrite> Connection<T, U> {
)
}
/// Receives a single line from this connection.
#[cfg(not(feature = "encoding"))]
pub fn recv(&self) -> Result<String> {
fn recv(&self) -> Result<String> {
let mut ret = String::new();
try!(self.reader.lock().unwrap().read_line(&mut ret));
if ret.is_empty() {
@ -153,55 +168,22 @@ impl<T: IrcRead, U: IrcWrite> Connection<T, U> {
}
}
/// Acquires the Reader lock.
pub fn reader<'a>(&'a self) -> MutexGuard<'a, T> {
self.reader.lock().unwrap()
fn written(&self) -> Option<String> {
None
}
/// Acquires the Writer lock.
pub fn writer<'a>(&'a self) -> MutexGuard<'a, U> {
self.writer.lock().unwrap()
}
}
/// Converts a Result<T, SslError> into an Result<T>.
#[cfg(feature = "ssl")]
fn ssl_to_io<T>(res: StdResult<T, SslError>) -> Result<T> {
match res {
Ok(x) => Ok(x),
Err(e) => Err(Error::new(ErrorKind::Other,
&format!("An SSL error occurred. ({})", e.description())[..]
)),
}
}
/// A trait defining the ability to reconnect.
pub trait Reconnect {
/// Reconnects to the specified host and port, dropping the current connection if necessary.
fn reconnect(&self, host: &str, port: u16) -> Result<()>;
}
macro_rules! noop_reconnect {
($T:ty, $U:ty) => {
impl Reconnect for Connection<$T, $U> {
fn reconnect(&self, _: &str, _: u16) -> Result<()> {
Ok(())
}
}
}
}
impl Reconnect for NetConnection {
fn reconnect(&self, host: &str, port: u16) -> Result<()> {
fn reconnect(&self) -> Result<()> {
let use_ssl = match self.reader.lock().unwrap().get_ref() {
&NetStream::UnsecuredTcpStream(_) => false,
#[cfg(feature = "ssl")]
&NetStream::SslTcpStream(_) => true,
};
let host = self.host.lock().unwrap();
let port = self.port.lock().unwrap();
let (reader, writer) = if use_ssl {
try!(Connection::connect_ssl_internal(host, port))
try!(NetConnection::connect_ssl_internal(&host, *port))
} else {
try!(Connection::connect_internal(host, port))
try!(NetConnection::connect_internal(&host, *port))
};
*self.reader.lock().unwrap() = reader;
*self.writer.lock().unwrap() = writer;
@ -209,11 +191,95 @@ impl Reconnect for NetConnection {
}
}
// TODO: replace all this with specialization when possible. :\
noop_reconnect!(Cursor<Vec<u8>>, Vec<u8>);
noop_reconnect!(Cursor<Vec<u8>>, Sink);
noop_reconnect!(BufReader<Empty>, Vec<u8>);
noop_reconnect!(BufReader<Empty>, Sink);
/// A mock connection for testing purposes.
pub struct MockConnection {
reader: Mutex<Cursor<Vec<u8>>>,
writer: Mutex<Vec<u8>>,
}
impl MockConnection {
/// Creates a new mock connection with the specified string in the read buffer.
pub fn new(input: &str) -> MockConnection {
MockConnection::from_byte_vec(input.as_bytes().to_vec())
}
/// Creates a new mock connection with the specified bytes in the read buffer.
pub fn from_byte_vec(input: Vec<u8>) -> MockConnection {
MockConnection {
reader: Mutex::new(Cursor::new(input)),
writer: Mutex::new(Vec::new()),
}
}
}
impl Connection for MockConnection {
#[cfg(feature = "encode")]
fn send(&self, msg: &str, encoding: &str) -> Result<()> {
let encoding = match encoding_from_whatwg_label(encoding) {
Some(enc) => enc,
None => return Err(Error::new(
ErrorKind::InvalidInput, &format!("Failed to find encoder. ({})", encoding)[..]
))
};
let data = match encoding.encode(msg, EncoderTrap::Replace) {
Ok(data) => data,
Err(data) => return Err(Error::new(ErrorKind::InvalidInput,
&format!("Failed to encode {} as {}.", data, encoding.name())[..]
))
};
let mut writer = self.writer.lock().unwrap();
try!(writer.write_all(&data));
writer.flush()
}
#[cfg(not(feature = "encode"))]
fn send(&self, msg: &str) -> Result<()> {
let mut writer = self.writer.lock().unwrap();
try!(writer.write_all(msg.as_bytes()));
writer.flush()
}
#[cfg(feature = "encoding")]
fn recv(&self, encoding: &str) -> Result<String> {
let encoding = match encoding_from_whatwg_label(encoding) {
Some(enc) => enc,
None => return Err(Error::new(
ErrorKind::InvalidInput, &format!("Failed to find decoder. ({})", encoding)[..]
))
};
let mut buf = Vec::new();
self.reader.lock().unwrap().read_until(b'\n', &mut buf).and_then(|_|
match encoding.decode(&buf, DecoderTrap::Replace) {
_ if buf.is_empty() => Err(Error::new(ErrorKind::Other, "EOF")),
Ok(data) => Ok(data),
Err(data) => return Err(Error::new(ErrorKind::InvalidInput,
&format!("Failed to decode {} as {}.", data, encoding.name())[..]
))
}
)
}
#[cfg(not(feature = "encoding"))]
fn recv(&self) -> Result<String> {
let mut ret = String::new();
try!(self.reader.lock().unwrap().read_line(&mut ret));
if ret.is_empty() {
Err(Error::new(ErrorKind::Other, "EOF"))
} else {
Ok(ret)
}
}
fn written(&self) -> Option<String> {
String::from_utf8(self.writer.lock().unwrap().clone()).ok()
}
fn reconnect(&self) -> Result<()> {
Ok(())
}
}
/// An abstraction over different networked streams.
pub enum NetStream {

View file

@ -174,7 +174,7 @@ impl Config {
self.ping_timeout.as_ref().map(|t| *t).unwrap_or(10)
}
/// Gets whether or not to use NickServ GHOST
/// Gets whether or not to attempt nickname reclamation using NickServ GHOST.
/// This defaults to false when not specified.
pub fn should_ghost(&self) -> bool {
self.should_ghost.as_ref().map(|u| *u).unwrap_or(false)

View file

@ -14,6 +14,7 @@ pub mod kinds {
/// Trait describing all possible Writers for this library.
pub trait IrcWrite: Write + Sized + Send + 'static {}
impl<T> IrcWrite for T where T: Write + Sized + Send + 'static {}
/// Trait describing all possible Readers for this library.
pub trait IrcRead: BufRead + Sized + Send + 'static {}
impl<T> IrcRead for T where T: BufRead + Sized + Send + 'static {}

View file

@ -5,28 +5,30 @@ use std::borrow::ToOwned;
use std::cell::Cell;
use std::collections::HashMap;
use std::error::Error as StdError;
use std::io::{BufReader, BufWriter, Error, ErrorKind, Result};
use std::io::{Error, ErrorKind, Result};
use std::path::Path;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::mpsc::{Receiver, Sender, TryRecvError, channel};
use std::thread::{JoinHandle, spawn};
use client::conn::{Connection, NetStream, Reconnect};
use client::conn::{Connection, NetConnection};
use client::data::{Command, Config, Message, Response, User};
use client::data::Command::{JOIN, NICK, NICKSERV, PART, PING, PRIVMSG, MODE};
use client::data::kinds::{IrcRead, IrcWrite};
use client::server::utils::ServerExt;
use time::{Duration, Timespec, Tm, now};
pub mod utils;
/// Trait describing core Server functionality.
pub trait Server<'a, T: IrcRead, U: IrcWrite> {
/// An interface for interacting with an IRC server.
pub trait Server {
/// Gets the configuration being used with this Server.
fn config(&self) -> &Config;
/// Sends a Command to this Server.
fn send<M: Into<Message>>(&self, message: M) -> Result<()> where Self: Sized;
/// Gets an Iterator over Messages received by this Server.
fn iter(&'a self) -> ServerIterator<'a, T, U>;
/// Gets an iterator over received messages.
fn iter<'a>(&'a self) -> Box<Iterator<Item = Result<Message>> + 'a>;
/// Gets a list of Users in the specified channel. This will be none if the channel is not
/// being tracked, or if tracking is not supported altogether. For best results, be sure to
/// request `multi-prefix` support from the server.
@ -34,21 +36,21 @@ pub trait Server<'a, T: IrcRead, U: IrcWrite> {
}
/// A thread-safe implementation of an IRC Server connection.
pub struct IrcServer<T: IrcRead, U: IrcWrite> {
pub struct IrcServer {
/// The channel for sending messages to write.
tx: Sender<Message>,
/// The internal, thread-safe server state.
state: Arc<ServerState<T, U>>,
state: Arc<ServerState>,
/// A thread-local count of reconnection attempts used for synchronization.
reconnect_count: Cell<u32>,
}
/// Thread-safe internal state for an IRC server connection.
struct ServerState<T: IrcRead, U: IrcWrite> {
struct ServerState {
/// A global copy of the channel for sending messages to write.
tx: Mutex<Option<Sender<Message>>>,
/// The thread-safe IRC connection.
conn: Connection<T, U>,
conn: Box<Connection + Send + Sync>,
/// The handle for the message sending thread.
write_handle: Mutex<Option<JoinHandle<()>>>,
/// The configuration used with this connection.
@ -65,11 +67,11 @@ struct ServerState<T: IrcRead, U: IrcWrite> {
last_ping_data: Mutex<Option<Timespec>>,
}
impl<T: IrcRead, U: IrcWrite> ServerState<T, U> where Connection<T, U>: Reconnect {
fn new(conn: Connection<T, U>, config: Config) -> ServerState<T, U> {
impl ServerState {
fn new<C>(conn: C, config: Config) -> ServerState where C: Connection + Send + Sync + 'static {
ServerState {
tx: Mutex::new(None),
conn: conn,
conn: Box::new(conn),
write_handle: Mutex::new(None),
config: config,
chanlists: Mutex::new(HashMap::new()),
@ -81,7 +83,7 @@ impl<T: IrcRead, U: IrcWrite> ServerState<T, U> where Connection<T, U>: Reconnec
}
fn reconnect(&self) -> Result<()> {
self.conn.reconnect(self.config.server(), self.config.port())
self.conn.reconnect()
}
fn action_taken(&self) {
@ -108,30 +110,27 @@ impl<T: IrcRead, U: IrcWrite> ServerState<T, U> where Connection<T, U>: Reconnec
}
}
/// An IrcServer over a buffered NetStream.
pub type NetIrcServer = IrcServer<BufReader<NetStream>, BufWriter<NetStream>>;
impl IrcServer<BufReader<NetStream>, BufWriter<NetStream>> {
impl IrcServer {
/// Creates a new IRC Server connection from the configuration at the specified path,
/// connecting immediately.
pub fn new<P: AsRef<Path>>(config: P) -> Result<NetIrcServer> {
pub fn new<P: AsRef<Path>>(config: P) -> Result<IrcServer> {
IrcServer::from_config(try!(Config::load(config)))
}
/// Creates a new IRC server connection from the specified configuration, connecting
/// immediately.
pub fn from_config(config: Config) -> Result<NetIrcServer> {
pub fn from_config(config: Config) -> Result<IrcServer> {
let conn = try!(if config.use_ssl() {
Connection::connect_ssl(config.server(), config.port())
NetConnection::connect_ssl(config.server(), config.port())
} else {
Connection::connect(config.server(), config.port())
NetConnection::connect(config.server(), config.port())
});
Ok(IrcServer::from_connection(config, conn))
}
}
impl<T: IrcRead, U: IrcWrite> Clone for IrcServer<T, U> {
fn clone(&self) -> IrcServer<T, U> {
impl Clone for IrcServer {
fn clone(&self) -> IrcServer {
IrcServer {
tx: self.tx.clone(),
state: self.state.clone(),
@ -140,7 +139,7 @@ impl<T: IrcRead, U: IrcWrite> Clone for IrcServer<T, U> {
}
}
impl<T: IrcRead, U: IrcWrite> Drop for ServerState<T, U> {
impl Drop for ServerState {
fn drop(&mut self) {
let _ = self.tx.lock().unwrap().take();
let mut guard = self.write_handle.lock().unwrap();
@ -150,7 +149,7 @@ impl<T: IrcRead, U: IrcWrite> Drop for ServerState<T, U> {
}
}
impl<'a, T: IrcRead, U: IrcWrite> Server<'a, T, U> for ServerState<T, U> where Connection<T, U>: Reconnect {
impl<'a> Server for ServerState {
fn config(&self) -> &Config {
&self.config
}
@ -164,7 +163,7 @@ impl<'a, T: IrcRead, U: IrcWrite> Server<'a, T, U> for ServerState<T, U> where C
}
}
fn iter(&'a self) -> ServerIterator<'a, T, U> {
fn iter(&self) -> Box<Iterator<Item = Result<Message>>> {
panic!("unimplemented")
}
@ -180,7 +179,7 @@ impl<'a, T: IrcRead, U: IrcWrite> Server<'a, T, U> for ServerState<T, U> where C
}
}
impl<'a, T: IrcRead, U: IrcWrite> Server<'a, T, U> for IrcServer<T, U> where Connection<T, U>: Reconnect {
impl Server for IrcServer {
fn config(&self) -> &Config {
&self.state.config
}
@ -189,8 +188,8 @@ impl<'a, T: IrcRead, U: IrcWrite> Server<'a, T, U> for IrcServer<T, U> where Con
self.tx.send(msg.into()).map_err(|e| Error::new(ErrorKind::Other, e))
}
fn iter(&'a self) -> ServerIterator<'a, T, U> {
ServerIterator::new(self)
fn iter<'a>(&'a self) -> Box<Iterator<Item = Result<Message>> + 'a> {
Box::new(ServerIterator::new(self))
}
#[cfg(not(feature = "nochanlists"))]
@ -205,9 +204,10 @@ impl<'a, T: IrcRead, U: IrcWrite> Server<'a, T, U> for IrcServer<T, U> where Con
}
}
impl<T: IrcRead, U: IrcWrite> IrcServer<T, U> where Connection<T, U>: Reconnect {
/// Creates an IRC server from the specified configuration, and any arbitrary Connection.
pub fn from_connection(config: Config, conn: Connection<T, U>) -> IrcServer<T, U> {
impl IrcServer {
/// Creates an IRC server from the specified configuration, and any arbitrary sync connection.
pub fn from_connection<C>(config: Config, conn: C) -> IrcServer
where C: Connection + Send + Sync + 'static {
let (tx, rx): (Sender<Message>, Receiver<Message>) = channel();
let state = Arc::new(ServerState::new(conn, config));
let weak = Arc::downgrade(&state);
@ -251,11 +251,11 @@ impl<T: IrcRead, U: IrcWrite> IrcServer<T, U> where Connection<T, U>: Reconnect
}
/// Gets a reference to the IRC server's connection.
pub fn conn(&self) -> &Connection<T, U> {
pub fn conn(&self) -> &Box<Connection + Send + Sync> {
&self.state.conn
}
/// Reconnects to the IRC server.
/// Reconnects to the IRC server, disconnecting if necessary.
pub fn reconnect(&self) -> Result<()> {
let mut reconnect_count = self.state.reconnect_count.lock().unwrap();
let res = if self.reconnect_count.get() == *reconnect_count {
@ -269,13 +269,13 @@ impl<T: IrcRead, U: IrcWrite> IrcServer<T, U> where Connection<T, U>: Reconnect
}
#[cfg(feature = "encode")]
fn write<M: Into<Message>>(state: &Arc<ServerState<T, U>>, msg: M) -> Result<()> {
state.conn.send(msg, state.config.encoding())
fn write<M: Into<Message>>(state: &Arc<ServerState>, msg: M) -> Result<()> {
state.conn.send(&msg.into().into_string(), state.config.encoding())
}
#[cfg(not(feature = "encode"))]
fn write<M: Into<Message>>(state: &Arc<ServerState<T, U>>, msg: M) -> Result<()> {
state.conn.send(msg)
fn write<M: Into<Message>>(state: &Arc<ServerState>, msg: M) -> Result<()> {
state.conn.send(&msg.into().into_string())
}
/// Returns a reference to the server state's channel lists.
@ -415,33 +415,14 @@ impl<T: IrcRead, U: IrcWrite> IrcServer<T, U> where Connection<T, U>: Reconnect
}
}
impl<T: IrcRead, U: IrcWrite + Clone> IrcServer<T, U> where Connection<T, U>: Reconnect {
/// Returns a copy of the server's connection after waiting for all pending messages to be
/// written. This function may cause unusual behavior when called on a server with operations
/// being performed on other threads. This function is destructive, and is primarily intended
/// for writing unit tests. Use it with care.
pub fn extract_writer(mut self) -> U {
let _ = self.state.tx.lock().unwrap().take();
// This is a terrible hack to get the real channel to drop.
// Otherwise, joining would never finish.
let (tx, _) = channel();
self.tx = tx;
let mut guard = self.state.write_handle.lock().unwrap();
if let Some(handle) = guard.take() {
handle.join().unwrap()
}
self.conn().writer().clone()
}
}
/// An Iterator over an IrcServer's incoming Messages.
pub struct ServerIterator<'a, T: IrcRead + 'a, U: IrcWrite + 'a> {
server: &'a IrcServer<T, U>
pub struct ServerIterator<'a> {
server: &'a IrcServer
}
impl<'a, T: IrcRead + 'a, U: IrcWrite + 'a> ServerIterator<'a, T, U> where Connection<T, U>: Reconnect {
impl<'a> ServerIterator<'a> {
/// Creates a new ServerIterator for the desired IrcServer.
pub fn new(server: &IrcServer<T, U>) -> ServerIterator<T, U> {
pub fn new(server: &'a IrcServer) -> ServerIterator {
ServerIterator { server: server }
}
@ -458,7 +439,7 @@ impl<'a, T: IrcRead + 'a, U: IrcWrite + 'a> ServerIterator<'a, T, U> where Conne
}
}
impl<'a, T: IrcRead + 'a, U: IrcWrite + 'a> Iterator for ServerIterator<'a, T, U> where Connection<T, U>: Reconnect {
impl<'a> Iterator for ServerIterator<'a> {
type Item = Result<Message>;
fn next(&mut self) -> Option<Result<Message>> {
loop {

View file

@ -5,12 +5,11 @@ use client::data::{Capability, NegotiationVersion};
use client::data::Command::{AUTHENTICATE, CAP, INVITE, JOIN, KICK, KILL, MODE, NICK, NOTICE};
use client::data::Command::{OPER, PASS, PONG, PRIVMSG, QUIT, SAMODE, SANICK, TOPIC, USER};
use client::data::command::CapSubCommand::{END, LS, REQ};
use client::data::kinds::{IrcRead, IrcWrite};
#[cfg(feature = "ctcp")] use time::get_time;
use client::server::Server;
/// Extensions for Server capabilities that make it easier to work directly with the protocol.
pub trait ServerExt<'a, T: IrcRead, U: IrcWrite>: Server<'a, T, U> {
pub trait ServerExt: Server {
/// Sends a request for a list of server capabilities for a specific IRCv3 version.
fn send_cap_ls(&self, version: NegotiationVersion) -> Result<()> where Self: Sized {
self.send(CAP(None, LS, match version {
@ -219,7 +218,7 @@ pub trait ServerExt<'a, T: IrcRead, U: IrcWrite>: Server<'a, T, U> {
}
}
impl<'a, T: IrcRead, U: IrcWrite, K: Server<'a, T, U>> ServerExt<'a, T, U> for K {}
impl<S> ServerExt for S where S: Server {}
#[cfg(test)]
mod test {