Updated to 0.7.9 and re-evaluated library stabilization.
This commit is contained in:
parent
d0b54f8119
commit
768c6f556f
10 changed files with 128 additions and 129 deletions
10
Cargo.toml
10
Cargo.toml
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
|
||||
name = "irc"
|
||||
version = "0.7.8"
|
||||
version = "0.7.9"
|
||||
description = "A simple, thread-safe IRC client library."
|
||||
authors = ["Aaron Weiss <aaronweiss74@gmail.com>"]
|
||||
license = "Unlicense"
|
||||
|
@ -18,19 +18,19 @@ encode = ["encoding"]
|
|||
ssl = ["openssl"]
|
||||
|
||||
[dependencies.rustc-serialize]
|
||||
rustc-serialize = "~0.2.5"
|
||||
rustc-serialize = "~0.2.7"
|
||||
|
||||
[dependencies.time]
|
||||
|
||||
time = "~0.1.10"
|
||||
time = "~0.1.12"
|
||||
optional = true
|
||||
|
||||
[dependencies.encoding]
|
||||
|
||||
encoding = "~0.2.16"
|
||||
encoding = "~0.2.18"
|
||||
optional = true
|
||||
|
||||
[dependencies.openssl]
|
||||
|
||||
openssl = "~0.2.13"
|
||||
openssl = "~0.2.15"
|
||||
optional = true
|
||||
|
|
31
src/conn.rs
31
src/conn.rs
|
@ -1,5 +1,5 @@
|
|||
//! Thread-safe connections on IrcStreams.
|
||||
#![experimental]
|
||||
#![stable]
|
||||
use std::error::Error;
|
||||
use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream};
|
||||
#[cfg(any(feature = "encode", feature = "ssl"))] use std::io::{IoError, IoErrorKind};
|
||||
|
@ -12,7 +12,7 @@ use data::message::ToMessage;
|
|||
#[cfg(feature = "ssl")] use openssl::ssl::error::SslError;
|
||||
|
||||
/// A thread-safe connection.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub struct Connection<T: IrcReader, U: IrcWriter> {
|
||||
reader: Mutex<T>,
|
||||
writer: Mutex<U>,
|
||||
|
@ -26,7 +26,7 @@ type NetReaderWriterPair = (BufferedReader<NetStream>, BufferedWriter<NetStream>
|
|||
|
||||
impl Connection<BufferedReader<NetStream>, BufferedWriter<NetStream>> {
|
||||
/// Creates a thread-safe TCP connection to the specified server.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn connect(host: &str, port: u16) -> IoResult<NetConnection> {
|
||||
let (reader, writer) = try!(Connection::connect_internal(host, port));
|
||||
Ok(Connection::new(reader, writer))
|
||||
|
@ -41,7 +41,7 @@ impl Connection<BufferedReader<NetStream>, BufferedWriter<NetStream>> {
|
|||
|
||||
/// Creates a thread-safe TCP connection to the specified server over SSL.
|
||||
/// If the library is compiled without SSL support, this method panics.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn connect_ssl(host: &str, port: u16) -> IoResult<NetConnection> {
|
||||
let (reader, writer) = try!(Connection::connect_ssl_internal(host, port));
|
||||
Ok(Connection::new(reader, writer))
|
||||
|
@ -64,6 +64,7 @@ impl Connection<BufferedReader<NetStream>, BufferedWriter<NetStream>> {
|
|||
}
|
||||
|
||||
/// Reconnects to the specified server, dropping the current connection.
|
||||
#[unstable = "Feature is relatively new."]
|
||||
pub fn reconnect(&self, host: &str, port: u16) -> IoResult<()> {
|
||||
let use_ssl = match self.reader.lock().unwrap().get_ref() {
|
||||
&NetStream::UnsecuredTcpStream(_) => false,
|
||||
|
@ -81,13 +82,13 @@ impl Connection<BufferedReader<NetStream>, BufferedWriter<NetStream>> {
|
|||
}
|
||||
|
||||
/// Sets the keepalive for the network stream.
|
||||
#[experimental]
|
||||
#[unstable = "Feature is relatively new."]
|
||||
pub fn set_keepalive(&self, delay_in_seconds: Option<usize>) -> IoResult<()> {
|
||||
self.mod_stream(|tcp| tcp.set_keepalive(delay_in_seconds))
|
||||
}
|
||||
|
||||
/// Sets the timeout for the network stream.
|
||||
#[experimental]
|
||||
#[unstable = "Feature is relatively new."]
|
||||
pub fn set_timeout(&self, timeout_ms: Option<u64>) {
|
||||
self.mod_stream(|tcp| Ok(tcp.set_timeout(timeout_ms))).unwrap(); // this cannot fail.
|
||||
}
|
||||
|
@ -104,7 +105,7 @@ impl Connection<BufferedReader<NetStream>, BufferedWriter<NetStream>> {
|
|||
|
||||
impl<T: IrcReader, U: IrcWriter> Connection<T, U> {
|
||||
/// Creates a new connection from an IrcReader and an IrcWriter.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn new(reader: T, writer: U) -> Connection<T, U> {
|
||||
Connection {
|
||||
reader: Mutex::new(reader),
|
||||
|
@ -113,7 +114,7 @@ impl<T: IrcReader, U: IrcWriter> Connection<T, U> {
|
|||
}
|
||||
|
||||
/// Sends a Message over this connection.
|
||||
#[experimental]
|
||||
#[experimental = "Design is very new."]
|
||||
#[cfg(feature = "encode")]
|
||||
pub fn send<M: ToMessage>(&self, to_msg: M, encoding: &str) -> IoResult<()> {
|
||||
let encoding = match encoding_from_whatwg_label(encoding) {
|
||||
|
@ -138,8 +139,8 @@ impl<T: IrcReader, U: IrcWriter> Connection<T, U> {
|
|||
writer.flush()
|
||||
}
|
||||
|
||||
/// Sends a message over this connection.
|
||||
#[experimental]
|
||||
/// Sends a message over this connection.
|
||||
#[experimental = "Design is very new."]
|
||||
#[cfg(not(feature = "encode"))]
|
||||
pub fn send<T: ToMessage>(&self, to_msg: T) -> IoResult<()> {
|
||||
let mut writer = self.writer.lock().unwrap();
|
||||
|
@ -148,7 +149,7 @@ impl<T: IrcReader, U: IrcWriter> Connection<T, U> {
|
|||
}
|
||||
|
||||
/// Receives a single line from this connection.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
#[cfg(feature = "encoding")]
|
||||
pub fn recv(&self, encoding: &str) -> IoResult<String> {
|
||||
let encoding = match encoding_from_whatwg_label(encoding) {
|
||||
|
@ -172,20 +173,20 @@ impl<T: IrcReader, U: IrcWriter> Connection<T, U> {
|
|||
}
|
||||
|
||||
/// Receives a single line from this connection.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
#[cfg(not(feature = "encoding"))]
|
||||
pub fn recv(&self) -> IoResult<String> {
|
||||
self.reader.lock().unwrap().read_line()
|
||||
}
|
||||
|
||||
/// Acquires the Reader lock.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn reader<'a>(&'a self) -> MutexGuard<'a, T> {
|
||||
self.reader.lock().unwrap()
|
||||
}
|
||||
|
||||
/// Acquires the Writer lock.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn writer<'a>(&'a self) -> MutexGuard<'a, U> {
|
||||
self.writer.lock().unwrap()
|
||||
}
|
||||
|
@ -205,7 +206,7 @@ fn ssl_to_io<T>(res: Result<T, SslError>) -> IoResult<T> {
|
|||
}
|
||||
|
||||
/// An abstraction over different networked streams.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub enum NetStream {
|
||||
/// An unsecured TcpStream.
|
||||
UnsecuredTcpStream(TcpStream),
|
||||
|
|
|
@ -12,72 +12,72 @@ use data::message::{Message, ToMessage};
|
|||
#[derive(Show, PartialEq)]
|
||||
pub enum Command<'a> {
|
||||
// 3.1 Connection Registration
|
||||
/// PASS password
|
||||
/// PASS :password
|
||||
PASS(&'a str),
|
||||
/// NICK nickname
|
||||
/// NICK :nickname
|
||||
NICK(&'a str),
|
||||
/// USER user mode * realname
|
||||
/// USER user mode * :realname
|
||||
USER(&'a str, &'a str, &'a str),
|
||||
/// OPER name password
|
||||
/// OPER name :password
|
||||
OPER(&'a str, &'a str),
|
||||
/// MODE nickname modes
|
||||
/// MODE channel modes [modeparams]
|
||||
MODE(&'a str, &'a str, Option<&'a str>),
|
||||
/// SERVICE nickname reserved distribution type reserved info
|
||||
/// SERVICE nickname reserved distribution type reserved :info
|
||||
SERVICE(&'a str, &'a str, &'a str, &'a str, &'a str, &'a str),
|
||||
/// QUIT Quit Message
|
||||
/// QUIT :comment
|
||||
QUIT(Option<&'a str>),
|
||||
/// SQUIT server comment
|
||||
/// SQUIT server :comment
|
||||
SQUIT(&'a str, &'a str),
|
||||
|
||||
// 3.2 Channel operations
|
||||
/// JOIN chanlist [chankeys]
|
||||
JOIN(&'a str, Option<&'a str>),
|
||||
/// PART chanlist [Part Message]
|
||||
/// PART chanlist :[comment]
|
||||
PART(&'a str, Option<&'a str>),
|
||||
// MODE is already defined.
|
||||
// MODE(&'a str, &'a str, Option<&'a str>),
|
||||
/// TOPIC channel [topic]
|
||||
/// TOPIC channel :[topic]
|
||||
TOPIC(&'a str, Option<&'a str>),
|
||||
/// NAMES [chanlist [target]]
|
||||
/// NAMES [chanlist :[target]]
|
||||
NAMES(Option<&'a str>, Option<&'a str>),
|
||||
/// LIST [chanlist [target]]
|
||||
/// LIST [chanlist :[target]]
|
||||
LIST(Option<&'a str>, Option<&'a str>),
|
||||
/// INVITE nickname channel
|
||||
INVITE(&'a str, &'a str),
|
||||
/// KICK chanlist userlist [comment]
|
||||
/// KICK chanlist userlist :[comment]
|
||||
KICK(&'a str, &'a str, Option<&'a str>),
|
||||
|
||||
// 3.3 Sending messages
|
||||
/// PRIVMSG msgtarget text to be sent
|
||||
/// PRIVMSG msgtarget :message
|
||||
PRIVMSG(&'a str, &'a str),
|
||||
/// NOTICE msgtarget text
|
||||
/// NOTICE msgtarget :message
|
||||
NOTICE(&'a str, &'a str),
|
||||
|
||||
// 3.4 Server queries and commands
|
||||
/// MOTD [target]
|
||||
/// MOTD :[target]
|
||||
MOTD(Option<&'a str>),
|
||||
/// LUSERS [mask [target]]
|
||||
/// LUSERS [mask :[target]]
|
||||
LUSERS(Option<&'a str>, Option<&'a str>),
|
||||
/// VERSION [target]
|
||||
/// VERSION :[target]
|
||||
VERSION(Option<&'a str>),
|
||||
/// STATS [query [target]]
|
||||
/// STATS [query :[target]]
|
||||
STATS(Option<&'a str>, Option<&'a str>),
|
||||
/// LINKS [[remote server] server mask]
|
||||
/// LINKS [[remote server] server :mask]
|
||||
LINKS(Option<&'a str>, Option<&'a str>),
|
||||
/// TIME [target]
|
||||
/// TIME :[target]
|
||||
TIME(Option<&'a str>),
|
||||
/// CONNECT target server port [remote server]
|
||||
/// CONNECT target server port :[remote server]
|
||||
CONNECT(&'a str, &'a str, Option<&'a str>),
|
||||
/// TRACE [target]
|
||||
/// TRACE :[target]
|
||||
TRACE(Option<&'a str>),
|
||||
/// ADMIN [target]
|
||||
/// ADMIN :[target]
|
||||
ADMIN(Option<&'a str>),
|
||||
/// INFO [target]
|
||||
/// INFO :[target]
|
||||
INFO(Option<&'a str>),
|
||||
|
||||
// 3.5 Service Query and Commands
|
||||
/// SERVLIST [mask [type]]
|
||||
/// SERVLIST [mask :[type]]
|
||||
SERVLIST(Option<&'a str>, Option<&'a str>),
|
||||
/// SQUERY servicename text
|
||||
SQUERY(&'a str, &'a str),
|
||||
|
@ -87,22 +87,22 @@ pub enum Command<'a> {
|
|||
WHO(Option<&'a str>, Option<bool>),
|
||||
/// WHOIS [target] masklist
|
||||
WHOIS(Option<&'a str>, &'a str),
|
||||
/// WHOWAS nicklist [count [target]]
|
||||
/// WHOWAS nicklist [count :[target]]
|
||||
WHOWAS(&'a str, Option<&'a str>, Option<&'a str>),
|
||||
|
||||
// 3.7 Miscellaneous messages
|
||||
/// KILL nickname comment
|
||||
/// KILL nickname :comment
|
||||
KILL(&'a str, &'a str),
|
||||
/// PING server1 [server2]
|
||||
/// PING server1 :[server2]
|
||||
PING(&'a str, Option<&'a str>),
|
||||
/// PONG server [server2]
|
||||
/// PONG server :[server2]
|
||||
PONG(&'a str, Option<&'a str>),
|
||||
/// ERROR error message
|
||||
/// ERROR :message
|
||||
ERROR(&'a str),
|
||||
|
||||
|
||||
// 4 Optional Features
|
||||
/// AWAY [text]
|
||||
/// AWAY :[message]
|
||||
AWAY(Option<&'a str>),
|
||||
/// REHASH
|
||||
REHASH,
|
||||
|
@ -110,11 +110,11 @@ pub enum Command<'a> {
|
|||
DIE,
|
||||
/// RESTART
|
||||
RESTART,
|
||||
/// SUMMON user [target [channel]]
|
||||
/// SUMMON user [target :[channel]]
|
||||
SUMMON(&'a str, Option<&'a str>, Option<&'a str>),
|
||||
/// USERS [target]
|
||||
/// USERS :[target]
|
||||
USERS(Option<&'a str>),
|
||||
/// WALLOPS Text to be sent
|
||||
/// WALLOPS :Text to be sent
|
||||
WALLOPS(&'a str),
|
||||
/// USERHOST space-separated nicklist
|
||||
USERHOST(Vec<&'a str>),
|
||||
|
@ -128,9 +128,9 @@ pub enum Command<'a> {
|
|||
SAMODE(&'a str, &'a str, Option<&'a str>),
|
||||
/// SANICK old nickname new nickname
|
||||
SANICK(&'a str, &'a str),
|
||||
/// SAPART nickname reason
|
||||
/// SAPART nickname :comment
|
||||
SAPART(&'a str, &'a str),
|
||||
/// SAQUIT nickname reason
|
||||
/// SAQUIT nickname :comment
|
||||
SAQUIT(&'a str, &'a str),
|
||||
/// NICKSERV message
|
||||
NICKSERV(&'a str),
|
||||
|
@ -146,7 +146,8 @@ pub enum Command<'a> {
|
|||
MEMOSERV(&'a str),
|
||||
|
||||
// Capabilities extension to IRCv3
|
||||
/// CAP COMMAND [param]
|
||||
/// CAP COMMAND :[param]
|
||||
#[experimental = "This command is not entirely specification compliant."]
|
||||
CAP(CapSubCommand, Option<&'a str>),
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use rustc_serialize::json::decode;
|
|||
|
||||
/// Configuration data.
|
||||
#[derive(Clone, RustcDecodable, Default, PartialEq, Show)]
|
||||
#[unstable]
|
||||
#[stable]
|
||||
pub struct Config {
|
||||
/// A list of the owners of the bot by nickname.
|
||||
pub owners: Option<Vec<String>>,
|
||||
|
@ -70,21 +70,21 @@ impl Config {
|
|||
|
||||
/// Gets the nickname specified in the configuration.
|
||||
/// This will panic if not specified.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn nickname(&self) -> &str {
|
||||
self.nickname.as_ref().map(|s| &s[]).unwrap()
|
||||
}
|
||||
|
||||
/// Gets the bot's nickserv password specified in the configuration.
|
||||
/// This defaults to an empty string when not specified.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn nick_password(&self) -> &str {
|
||||
self.nick_password.as_ref().map(|s| &s[]).unwrap_or("")
|
||||
}
|
||||
|
||||
/// Gets the alternate nicknames specified in the configuration.
|
||||
/// This defaults to an empty vector when not specified.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn get_alternate_nicknames(&self) -> Vec<&str> {
|
||||
self.alt_nicks.as_ref().map(|v| v.iter().map(|s| &s[]).collect()).unwrap_or(vec![])
|
||||
}
|
||||
|
@ -92,28 +92,28 @@ impl Config {
|
|||
|
||||
/// Gets the username specified in the configuration.
|
||||
/// This defaults to the user's nickname when not specified.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn username(&self) -> &str {
|
||||
self.username.as_ref().map(|s| &s[]).unwrap_or(self.nickname())
|
||||
}
|
||||
|
||||
/// Gets the real name specified in the configuration.
|
||||
/// This defaults to the user's nickname when not specified.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn real_name(&self) -> &str {
|
||||
self.realname.as_ref().map(|s| &s[]).unwrap_or(self.nickname())
|
||||
}
|
||||
|
||||
/// Gets the address of the server specified in the configuration.
|
||||
/// This panics when not specified.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn server(&self) -> &str {
|
||||
self.server.as_ref().map(|s| &s[]).unwrap()
|
||||
}
|
||||
|
||||
/// Gets the port of the server specified in the configuration.
|
||||
/// This defaults to 6667 (or 6697 if use_ssl is specified as true) when not specified.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn port(&self) -> u16 {
|
||||
self.port.as_ref().map(|p| *p).unwrap_or(if self.use_ssl() {
|
||||
6697
|
||||
|
@ -124,35 +124,35 @@ impl Config {
|
|||
|
||||
/// Gets the server password specified in the configuration.
|
||||
/// This defaults to a blank string when not specified.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn password(&self) -> &str {
|
||||
self.password.as_ref().map(|s| &s[]).unwrap_or("")
|
||||
}
|
||||
|
||||
/// Gets whether or not to use SSL with this connection.
|
||||
/// This defaults to false when not specified.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn use_ssl(&self) -> bool {
|
||||
self.use_ssl.as_ref().map(|u| *u).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Gets the encoding to use for this connection. This requires the encode feature to work.
|
||||
/// This defaults to UTF-8 when not specified.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn encoding(&self) -> &str {
|
||||
self.encoding.as_ref().map(|s| &s[]).unwrap_or("UTF-8")
|
||||
}
|
||||
|
||||
/// Gets the channels to join upon connection.
|
||||
/// This defaults to an empty vector if it's not specified.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn channels(&self) -> Vec<&str> {
|
||||
self.channels.as_ref().map(|v| v.iter().map(|s| &s[]).collect()).unwrap_or(vec![])
|
||||
}
|
||||
|
||||
/// Gets the string to be sent in response to CTCP USERINFO requests.
|
||||
/// This defaults to an empty string when not specified.
|
||||
#[experimental]
|
||||
#[unstable = "Feature is still relatively new."]
|
||||
pub fn user_info(&self) -> &str {
|
||||
self.user_info.as_ref().map(|s| &s[]).unwrap_or("")
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ impl Config {
|
|||
/// Looks up the specified string in the options map.
|
||||
/// This uses indexing, and thus panics when the string is not present.
|
||||
/// This will also panic if used and there are no options.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn get_option(&self, option: &str) -> &str {
|
||||
self.options.as_ref().map(|o| &o[option.to_owned()][]).unwrap()
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::borrow::ToOwned;
|
|||
use std::str::FromStr;
|
||||
|
||||
/// IRC Message data.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
#[derive(Clone, PartialEq, Show)]
|
||||
pub struct Message {
|
||||
/// The message prefix (or source) as defined by [RFC 2812](http://tools.ietf.org/html/rfc2812).
|
||||
|
@ -20,7 +20,7 @@ pub struct Message {
|
|||
|
||||
impl Message {
|
||||
/// Creates a new Message.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn new(prefix: Option<&str>, command: &str, args: Option<Vec<&str>>, suffix: Option<&str>)
|
||||
-> Message {
|
||||
Message {
|
||||
|
@ -32,13 +32,13 @@ impl Message {
|
|||
}
|
||||
|
||||
/// Gets the nickname of the message source, if it exists.
|
||||
#[experimental]
|
||||
#[experimental = "Feature is new."]
|
||||
pub fn get_source_nickname(&self) -> Option<&str> {
|
||||
self.prefix.as_ref().and_then(|s| s.find('!').map(|i| &s[..i]))
|
||||
}
|
||||
|
||||
/// Converts a Message into a String according to the IRC protocol.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn into_string(&self) -> String {
|
||||
let mut ret = String::new();
|
||||
if let Some(ref prefix) = self.prefix {
|
||||
|
@ -98,6 +98,7 @@ impl FromStr for Message {
|
|||
}
|
||||
|
||||
/// A trait representing the ability to be converted into a Message.
|
||||
#[experimental = "Design is new."]
|
||||
pub trait ToMessage {
|
||||
/// Converts this to a Message.
|
||||
fn to_message(&self) -> Message;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! Data related to IRC functionality.
|
||||
#![unstable]
|
||||
#![stable]
|
||||
|
||||
pub use data::command::Command;
|
||||
pub use data::config::Config;
|
||||
|
@ -9,18 +9,18 @@ pub use data::user::{AccessLevel, User};
|
|||
|
||||
pub mod kinds {
|
||||
//! Trait definitions of appropriate Writers and Buffers for use with this library.
|
||||
#![unstable]
|
||||
#![stable]
|
||||
|
||||
/// Trait describing all possible Writers for this library.
|
||||
#[unstable]
|
||||
#[stable]
|
||||
pub trait IrcWriter: Writer + Sized + Send + 'static {}
|
||||
impl<T> IrcWriter for T where T: Writer + Sized + Send + 'static {}
|
||||
/// Trait describing all possible Readers for this library.
|
||||
#[unstable]
|
||||
#[stable]
|
||||
pub trait IrcReader: Buffer + Sized + Send + 'static {}
|
||||
impl<T> IrcReader for T where T: Buffer + Sized + Send + 'static {}
|
||||
/// Trait describing all possible Streams for this library.
|
||||
#[unstable]
|
||||
#[unstable = "May be removed."]
|
||||
pub trait IrcStream: IrcWriter + IrcReader {}
|
||||
impl<T> IrcStream for T where T: IrcWriter + IrcReader {}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! Enumeration of all the possible server responses.
|
||||
#![unstable]
|
||||
#![stable]
|
||||
#![allow(non_camel_case_types)]
|
||||
use std::num::FromPrimitive;
|
||||
use std::str::FromStr;
|
||||
|
@ -9,7 +9,7 @@ use data::message::Message;
|
|||
/// All commands are documented with their expected form from the RFC.
|
||||
#[derive(Copy, Show, PartialEq, FromPrimitive)]
|
||||
#[repr(u16)]
|
||||
#[unstable]
|
||||
#[stable]
|
||||
pub enum Response {
|
||||
// Expected replies
|
||||
/// 001 Welcome to the Internet Relay Network <nick>!<user>@<host>
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::cmp::Ordering::{Less, Equal, Greater};
|
|||
use std::str::FromStr;
|
||||
|
||||
/// IRC User data.
|
||||
#[unstable]
|
||||
#[stable]
|
||||
#[derive(Clone, Show)]
|
||||
pub struct User {
|
||||
/// The user's nickname.
|
||||
|
@ -48,19 +48,19 @@ impl User {
|
|||
}
|
||||
|
||||
/// Gets the user's highest access level.
|
||||
#[experimental]
|
||||
#[unstable = "API may change."]
|
||||
pub fn highest_access_level(&self) -> AccessLevel {
|
||||
self.highest_access_level
|
||||
}
|
||||
|
||||
/// Gets all the user's access levels.
|
||||
#[experimental]
|
||||
#[unstable = "API may change."]
|
||||
pub fn access_levels(&self) -> Vec<AccessLevel> {
|
||||
self.access_levels.clone()
|
||||
}
|
||||
|
||||
/// Updates the user's access level.
|
||||
#[unstable]
|
||||
#[unstable = "API may change."]
|
||||
pub fn update_access_level(&mut self, mode: &str) {
|
||||
match mode {
|
||||
"+q" => self.add_access_level(AccessLevel::Owner),
|
||||
|
@ -77,6 +77,7 @@ impl User {
|
|||
}
|
||||
}
|
||||
|
||||
/// Adds an access level to the list, and updates the highest level if necessary.
|
||||
fn add_access_level(&mut self, level: AccessLevel) {
|
||||
if level > self.highest_access_level() {
|
||||
self.highest_access_level = level
|
||||
|
@ -84,6 +85,7 @@ impl User {
|
|||
self.access_levels.push(level.clone())
|
||||
}
|
||||
|
||||
/// Removes an access level from the list, and updates the highest level if necessary.
|
||||
fn sub_access_level(&mut self, level: AccessLevel) {
|
||||
if let Some(n) = self.access_levels[].position_elem(&level) {
|
||||
self.access_levels.swap_remove(n);
|
||||
|
@ -179,6 +181,7 @@ impl FromStr for AccessLevel {
|
|||
}
|
||||
}
|
||||
|
||||
/// An iterator used to parse access levels from strings.
|
||||
struct AccessLevelIterator<'a> {
|
||||
value: &'a str,
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! Interface for working with IRC Servers
|
||||
#![experimental]
|
||||
#![stable]
|
||||
use std::borrow::ToOwned;
|
||||
use std::collections::HashMap;
|
||||
use std::io::{BufferedReader, BufferedWriter, IoError, IoErrorKind, IoResult};
|
||||
|
@ -13,7 +13,7 @@ use data::kinds::{IrcReader, IrcWriter};
|
|||
pub mod utils;
|
||||
|
||||
/// Trait describing core Server functionality.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub trait Server<'a, T, U> {
|
||||
/// Gets the configuration being used with this Server.
|
||||
fn config(&self) -> &Config;
|
||||
|
@ -26,7 +26,7 @@ pub trait Server<'a, T, U> {
|
|||
}
|
||||
|
||||
/// A thread-safe implementation of an IRC Server connection.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub struct IrcServer<T: IrcReader, U: IrcWriter> {
|
||||
/// The thread-safe IRC connection.
|
||||
conn: Connection<T, U>,
|
||||
|
@ -44,14 +44,14 @@ pub type NetIrcServer = IrcServer<BufferedReader<NetStream>, BufferedWriter<NetS
|
|||
impl IrcServer<BufferedReader<NetStream>, BufferedWriter<NetStream>> {
|
||||
/// Creates a new IRC Server connection from the configuration at the specified path,
|
||||
/// connecting immediately.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn new(config: &str) -> IoResult<NetIrcServer> {
|
||||
IrcServer::from_config(try!(Config::load_utf8(config)))
|
||||
}
|
||||
|
||||
/// Creates a new IRC server connection from the specified configuration, connecting
|
||||
/// immediately.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn from_config(config: Config) -> IoResult<NetIrcServer> {
|
||||
let conn = try!(if config.use_ssl() {
|
||||
Connection::connect_ssl(config.server(), config.port())
|
||||
|
@ -63,7 +63,7 @@ impl IrcServer<BufferedReader<NetStream>, BufferedWriter<NetStream>> {
|
|||
}
|
||||
|
||||
/// Reconnects to the IRC server.
|
||||
#[experimental]
|
||||
#[unstable = "Feature is relatively new."]
|
||||
pub fn reconnect(&self) -> IoResult<()> {
|
||||
self.conn.reconnect(self.config().server(), self.config.port())
|
||||
}
|
||||
|
@ -95,20 +95,19 @@ impl<'a, T: IrcReader, U: IrcWriter> Server<'a, T, U> for IrcServer<T, U> {
|
|||
|
||||
impl<T: IrcReader, U: IrcWriter> IrcServer<T, U> {
|
||||
/// Creates an IRC server from the specified configuration, and any arbitrary Connection.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn from_connection(config: Config, conn: Connection<T, U>) -> IrcServer<T, U> {
|
||||
IrcServer { conn: conn, config: config, chanlists: Mutex::new(HashMap::new()),
|
||||
alt_nick_index: RwLock::new(0) }
|
||||
}
|
||||
|
||||
/// Gets a reference to the IRC server's connection.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn conn(&self) -> &Connection<T, U> {
|
||||
&self.conn
|
||||
}
|
||||
|
||||
/// Handles messages internally for basic bot functionality.
|
||||
#[experimental]
|
||||
fn handle_message(&self, msg: &Message) {
|
||||
if let Some(resp) = Response::from_message(msg) {
|
||||
if resp == Response::RPL_NAMREPLY {
|
||||
|
@ -179,7 +178,6 @@ impl<T: IrcReader, U: IrcWriter> IrcServer<T, U> {
|
|||
}
|
||||
|
||||
/// Handles CTCP requests if the CTCP feature is enabled.
|
||||
#[experimental]
|
||||
#[cfg(feature = "ctcp")]
|
||||
fn handle_ctcp(&self, msg: &Message) {
|
||||
let source = match msg.prefix {
|
||||
|
@ -220,31 +218,26 @@ impl<T: IrcReader, U: IrcWriter> IrcServer<T, U> {
|
|||
}
|
||||
|
||||
/// Sends a CTCP-escaped message.
|
||||
#[experimental]
|
||||
#[cfg(feature = "ctcp")]
|
||||
fn send_ctcp(&self, target: &str, msg: &str) {
|
||||
self.send(Command::NOTICE(target, &format!("\u{001}{}\u{001}", msg)[])).unwrap();
|
||||
}
|
||||
|
||||
/// Handles CTCP requests if the CTCP feature is enabled.
|
||||
#[experimental]
|
||||
#[cfg(not(feature = "ctcp"))]
|
||||
fn handle_ctcp(&self, _: &Message) {}
|
||||
#[cfg(not(feature = "ctcp"))] fn handle_ctcp(&self, _: &Message) {}
|
||||
}
|
||||
|
||||
/// An Iterator over an IrcServer's incoming Messages.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub struct ServerIterator<'a, T: IrcReader, U: IrcWriter> {
|
||||
server: &'a IrcServer<T, U>
|
||||
}
|
||||
|
||||
impl<'a, T: IrcReader, U: IrcWriter> ServerIterator<'a, T, U> {
|
||||
/// Creates a new ServerIterator for the desired IrcServer.
|
||||
#[experimental]
|
||||
#[experimental = "Design will change to accomodate new behavior."]
|
||||
pub fn new(server: &IrcServer<T, U>) -> ServerIterator<T, U> {
|
||||
ServerIterator {
|
||||
server: server
|
||||
}
|
||||
ServerIterator { server: server }
|
||||
}
|
||||
|
||||
/// Gets the next line from the connection.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! Utilities and shortcuts for working with IRC servers.
|
||||
#![experimental]
|
||||
#![stable]
|
||||
|
||||
use std::io::IoResult;
|
||||
use data::{Command, Config, User};
|
||||
|
@ -12,7 +12,7 @@ use server::{Server, ServerIterator};
|
|||
|
||||
/// Functionality-providing wrapper for Server.
|
||||
/// Wrappers are currently not thread-safe, and should be created per-thread, as needed.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub struct Wrapper<'a, T: IrcReader, U: IrcWriter> {
|
||||
server: &'a (Server<'a, T, U> + 'a)
|
||||
}
|
||||
|
@ -37,13 +37,13 @@ impl<'a, T: IrcReader, U: IrcWriter> Server<'a, T, U> for Wrapper<'a, T, U> {
|
|||
|
||||
impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
||||
/// Creates a new Wrapper from the given Server.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn new(server: &'a Server<'a, T, U>) -> Wrapper<'a, T, U> {
|
||||
Wrapper { server: server }
|
||||
}
|
||||
|
||||
/// Sends a NICK and USER to identify.
|
||||
#[experimental]
|
||||
#[unstable = "Capabilities requests may be moved outside of identify."]
|
||||
pub fn identify(&self) -> IoResult<()> {
|
||||
// We'll issue a CAP REQ for multi-prefix support to improve access level tracking.
|
||||
try!(self.server.send(CAP(REQ, Some("multi-prefix"))));
|
||||
|
@ -58,25 +58,25 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
}
|
||||
|
||||
/// Sends a PONG with the specified message.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn send_pong(&self, msg: &str) -> IoResult<()> {
|
||||
self.server.send(PONG(msg, None))
|
||||
}
|
||||
|
||||
/// Joins the specified channel or chanlist.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn send_join(&self, chanlist: &str) -> IoResult<()> {
|
||||
self.server.send(JOIN(chanlist, None))
|
||||
}
|
||||
|
||||
/// Attempts to oper up using the specified username and password.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn send_oper(&self, username: &str, password: &str) -> IoResult<()> {
|
||||
self.server.send(OPER(username, password))
|
||||
}
|
||||
|
||||
/// Sends a message to the specified target.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn send_privmsg(&self, target: &str, message: &str) -> IoResult<()> {
|
||||
for line in message.split_str("\r\n") {
|
||||
try!(self.server.send(PRIVMSG(target, line)))
|
||||
|
@ -85,7 +85,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
}
|
||||
|
||||
/// Sends a notice to the specified target.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn send_notice(&self, target: &str, message: &str) -> IoResult<()> {
|
||||
for line in message.split_str("\r\n") {
|
||||
try!(self.server.send(NOTICE(target, line)))
|
||||
|
@ -95,7 +95,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
|
||||
/// Sets the topic of a channel or requests the current one.
|
||||
/// If `topic` is an empty string, it won't be included in the message.
|
||||
#[experimental]
|
||||
#[unstable = "Design may change."]
|
||||
pub fn send_topic(&self, channel: &str, topic: &str) -> IoResult<()> {
|
||||
self.server.send(TOPIC(channel, if topic.len() == 0 {
|
||||
None
|
||||
|
@ -105,7 +105,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
}
|
||||
|
||||
/// Kills the target with the provided message.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn send_kill(&self, target: &str, message: &str) -> IoResult<()> {
|
||||
self.server.send(KILL(target, message))
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
|
||||
/// Changes the mode of the target.
|
||||
/// If `modeparmas` is an empty string, it won't be included in the message.
|
||||
#[experimental]
|
||||
#[unstable = "Design may change."]
|
||||
pub fn send_mode(&self, target: &str, mode: &str, modeparams: &str) -> IoResult<()> {
|
||||
self.server.send(MODE(target, mode, if modeparams.len() == 0 {
|
||||
None
|
||||
|
@ -134,7 +134,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
|
||||
/// Changes the mode of the target by force.
|
||||
/// If `modeparams` is an empty string, it won't be included in the message.
|
||||
#[experimental]
|
||||
#[unstable = "Design may change."]
|
||||
pub fn send_samode(&self, target: &str, mode: &str, modeparams: &str) -> IoResult<()> {
|
||||
self.server.send(SAMODE(target, mode, if modeparams.len() == 0 {
|
||||
None
|
||||
|
@ -144,20 +144,20 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
}
|
||||
|
||||
/// Forces a user to change from the old nickname to the new nickname.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn send_sanick(&self, old_nick: &str, new_nick: &str) -> IoResult<()> {
|
||||
self.server.send(SANICK(old_nick, new_nick))
|
||||
}
|
||||
|
||||
/// Invites a user to the specified channel.
|
||||
#[experimental]
|
||||
#[stable]
|
||||
pub fn send_invite(&self, nick: &str, chan: &str) -> IoResult<()> {
|
||||
self.server.send(INVITE(nick, chan))
|
||||
}
|
||||
|
||||
/// Quits the server entirely with a message.
|
||||
/// This defaults to `Powered by Rust.` if none is specified.
|
||||
#[experimental]
|
||||
#[unstable = "Design may change."]
|
||||
pub fn send_quit(&self, msg: &str) -> IoResult<()> {
|
||||
self.server.send(QUIT(Some(if msg.len() == 0 {
|
||||
"Powered by Rust."
|
||||
|
@ -168,7 +168,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
|
||||
/// Sends a CTCP-escaped message to the specified target.
|
||||
/// This requires the CTCP feature to be enabled.
|
||||
#[experimental]
|
||||
#[unstable = "Feature is relatively new."]
|
||||
#[cfg(feature = "ctcp")]
|
||||
pub fn send_ctcp(&self, target: &str, msg: &str) -> IoResult<()> {
|
||||
self.send_privmsg(target, &format!("\u{001}{}\u{001}", msg)[])
|
||||
|
@ -176,7 +176,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
|
||||
/// Sends an action command to the specified target.
|
||||
/// This requires the CTCP feature to be enabled.
|
||||
#[experimental]
|
||||
#[unstable = "Feature is relatively new."]
|
||||
#[cfg(feature = "ctcp")]
|
||||
pub fn send_action(&self, target: &str, msg: &str) -> IoResult<()> {
|
||||
self.send_ctcp(target, &format!("ACTION {}", msg)[])
|
||||
|
@ -184,7 +184,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
|
||||
/// Sends a finger request to the specified target.
|
||||
/// This requires the CTCP feature to be enabled.
|
||||
#[experimental]
|
||||
#[unstable = "Feature is relatively new."]
|
||||
#[cfg(feature = "ctcp")]
|
||||
pub fn send_finger(&self, target: &str) -> IoResult<()> {
|
||||
self.send_ctcp(target, "FINGER")
|
||||
|
@ -192,7 +192,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
|
||||
/// Sends a version request to the specified target.
|
||||
/// This requires the CTCP feature to be enabled.
|
||||
#[experimental]
|
||||
#[unstable = "Feature is relatively new."]
|
||||
#[cfg(feature = "ctcp")]
|
||||
pub fn send_version(&self, target: &str) -> IoResult<()> {
|
||||
self.send_ctcp(target, "VERSION")
|
||||
|
@ -200,7 +200,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
|
||||
/// Sends a source request to the specified target.
|
||||
/// This requires the CTCP feature to be enabled.
|
||||
#[experimental]
|
||||
#[unstable = "Feature is relatively new."]
|
||||
#[cfg(feature = "ctcp")]
|
||||
pub fn send_source(&self, target: &str) -> IoResult<()> {
|
||||
self.send_ctcp(target, "SOURCE")
|
||||
|
@ -208,7 +208,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
|
||||
/// Sends a user info request to the specified target.
|
||||
/// This requires the CTCP feature to be enabled.
|
||||
#[experimental]
|
||||
#[unstable = "Feature is relatively new."]
|
||||
#[cfg(feature = "ctcp")]
|
||||
pub fn send_user_info(&self, target: &str) -> IoResult<()> {
|
||||
self.send_ctcp(target, "USERINFO")
|
||||
|
@ -216,7 +216,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
|
||||
/// Sends a finger request to the specified target.
|
||||
/// This requires the CTCP feature to be enabled.
|
||||
#[experimental]
|
||||
#[unstable = "Feature is relatively new."]
|
||||
#[cfg(feature = "ctcp")]
|
||||
pub fn send_ctcp_ping(&self, target: &str) -> IoResult<()> {
|
||||
let time = get_time();
|
||||
|
@ -225,7 +225,7 @@ impl<'a, T: IrcReader, U: IrcWriter> Wrapper<'a, T, U> {
|
|||
|
||||
/// Sends a time request to the specified target.
|
||||
/// This requires the CTCP feature to be enabled.
|
||||
#[experimental]
|
||||
#[unstable = "Feature is relatively new."]
|
||||
#[cfg(feature = "ctcp")]
|
||||
pub fn send_time(&self, target: &str) -> IoResult<()> {
|
||||
self.send_ctcp(target, "TIME")
|
||||
|
|
Loading…
Reference in a new issue