Added documentation everywhere.

This commit is contained in:
Aaron Weiss 2014-11-03 00:52:15 -05:00
parent 63f4ca5097
commit 5bbde7e96c
7 changed files with 141 additions and 53 deletions

View file

@ -1,14 +1,20 @@
//! Thread-safe connections on any IrcWriters and IrcReaders
#![experimental]
use std::sync::Mutex; use std::sync::Mutex;
use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream};
use data::kinds::{IrcWriter, IrcReader}; use data::kinds::{IrcWriter, IrcReader};
use data::message::Message; use data::message::Message;
/// A thread-safe connection
#[experimental]
pub struct Connection<T, U> where T: IrcWriter, U: IrcReader { pub struct Connection<T, U> where T: IrcWriter, U: IrcReader {
writer: Mutex<T>, writer: Mutex<T>,
reader: Mutex<U>, reader: Mutex<U>,
} }
impl Connection<BufferedWriter<TcpStream>, BufferedReader<TcpStream>> { impl Connection<BufferedWriter<TcpStream>, BufferedReader<TcpStream>> {
/// Creates a thread-safe TCP connection to the specified server
#[experimental]
pub fn connect(host: &str, port: u16) -> IoResult<Connection<BufferedWriter<TcpStream>, BufferedReader<TcpStream>>> { pub fn connect(host: &str, port: u16) -> IoResult<Connection<BufferedWriter<TcpStream>, BufferedReader<TcpStream>>> {
let socket = try!(TcpStream::connect(host, port)); let socket = try!(TcpStream::connect(host, port));
Ok(Connection::new(BufferedWriter::new(socket.clone()), BufferedReader::new(socket))) Ok(Connection::new(BufferedWriter::new(socket.clone()), BufferedReader::new(socket)))
@ -16,6 +22,8 @@ impl Connection<BufferedWriter<TcpStream>, BufferedReader<TcpStream>> {
} }
impl<T, U> Connection<T, U> where T: IrcWriter, U: IrcReader { impl<T, U> Connection<T, U> where T: IrcWriter, U: IrcReader {
/// Creates a new connection from any arbitrary IrcWriter and IrcReader
#[experimental]
pub fn new(writer: T, reader: U) -> Connection<T, U> { pub fn new(writer: T, reader: U) -> Connection<T, U> {
Connection { Connection {
writer: Mutex::new(writer), writer: Mutex::new(writer),
@ -23,12 +31,16 @@ impl<T, U> Connection<T, U> where T: IrcWriter, U: IrcReader {
} }
} }
/// Sends a Message over this connection
#[experimental]
pub fn send(&self, message: Message) -> IoResult<()> { pub fn send(&self, message: Message) -> IoResult<()> {
let mut send = self.writer.lock(); let mut send = self.writer.lock();
try!(send.write_str(message.into_string()[])); try!(send.write_str(message.into_string()[]));
send.flush() send.flush()
} }
/// Receives a single line from this connection
#[experimental]
pub fn recv(&self) -> IoResult<String> { pub fn recv(&self) -> IoResult<String> {
self.reader.lock().read_line() self.reader.lock().read_line()
} }

View file

@ -1,101 +1,104 @@
//! Enumeration of all available client commands
#![stable]
use std::io::{InvalidInput, IoError, IoResult}; use std::io::{InvalidInput, IoError, IoResult};
use data::message::Message; use data::message::Message;
/// List of all client commands as defined in RFC 2812. /// List of all client commands as defined in [RFC 2812](http://tools.ietf.org/html/rfc2812)
#[stable]
#[deriving(Show, PartialEq)] #[deriving(Show, PartialEq)]
pub enum Command { pub enum Command {
// 3.1 Connection Registration // 3.1 Connection Registration
/// PASS <password> /// PASS password
PASS(String), PASS(String),
/// NICK <nickname> /// NICK nickname
NICK(String), NICK(String),
/// USER <user> <mode> * <realname> /// USER user mode * realname
USER(String, String, String), USER(String, String, String),
/// OPER <name> <password> /// OPER name password
OPER(String, String), OPER(String, String),
/// MODE <nickname> <modes> /// MODE nickname modes
/// MODE <channel> <modes> [<modeparams>] /// MODE channel modes [modeparams]
MODE(String, String, Option<String>), MODE(String, String, Option<String>),
/// SERVICE <nickname> <reserved> <distribution> <type> <reserved> <info> /// SERVICE nickname reserved distribution type reserved info
SERVICE(String, String, String, String, String, String), SERVICE(String, String, String, String, String, String),
/// QUIT <Quit Message> /// QUIT Quit Message
QUIT(Option<String>), QUIT(Option<String>),
/// SQUIT <server> <comment> /// SQUIT server comment
SQUIT(String, String), SQUIT(String, String),
// 3.2 Channel operations // 3.2 Channel operations
/// JOIN <chanlist> [<chankeys>] /// JOIN chanlist [chankeys]
JOIN(String, Option<String>), JOIN(String, Option<String>),
/// PART <chanlist> [<Part Message>] /// PART chanlist [Part Message]
PART(String, Option<String>), PART(String, Option<String>),
// MODE is already defined. // MODE is already defined.
// MODE(String, String, Option<String>), // MODE(String, String, Option<String>),
/// TOPIC <channel> [<topic>] /// TOPIC channel [topic]
TOPIC(String, Option<String>), TOPIC(String, Option<String>),
/// NAMES [<chanlist> [<target>]] /// NAMES [chanlist [target]]
NAMES(Option<String>, Option<String>), NAMES(Option<String>, Option<String>),
/// LIST [<chanlist> [<target>]] /// LIST [chanlist [target]]
LIST(Option<String>, Option<String>), LIST(Option<String>, Option<String>),
/// INVITE <nickname> <channel> /// INVITE nickname channel
INVITE(String, String), INVITE(String, String),
/// KICK <chanlist> <userlist> [<comment>] /// KICK chanlist userlist [comment]
KICK(String, String, Option<String>), KICK(String, String, Option<String>),
// 3.3 Sending messages // 3.3 Sending messages
/// PRIVMSG <msgtarget> <text to be sent> /// PRIVMSG msgtarget text to be sent
PRIVMSG(String, String), PRIVMSG(String, String),
/// NOTICE <msgtarget> <text> /// NOTICE msgtarget text
NOTICE(String, String), NOTICE(String, String),
// 3.4 Server queries and commands // 3.4 Server queries and commands
/// MOTD [<target>] /// MOTD [target]
MOTD(Option<String>), MOTD(Option<String>),
/// LUSERS [<mask> [<target>]] /// LUSERS [mask [target]]
LUSERS(Option<String>, Option<String>), LUSERS(Option<String>, Option<String>),
/// VERSION [<target>] /// VERSION [target]
VERSION(Option<String>), VERSION(Option<String>),
/// STATS [<query> [<target>]] /// STATS [query [target]]
STATS(Option<String>, Option<String>), STATS(Option<String>, Option<String>),
/// LINKS [[<remote server>] <server mask>] /// LINKS [[remote server] server mask]
LINKS(Option<String>, Option<String>), LINKS(Option<String>, Option<String>),
/// TIME [<target>] /// TIME [target]
TIME(Option<String>), TIME(Option<String>),
/// CONNECT <target server> <port> [<remote server>] /// CONNECT target server port [remote server]
CONNECT(String, String, Option<String>), CONNECT(String, String, Option<String>),
/// TRACE [<target>] /// TRACE [target]
TRACE(Option<String>), TRACE(Option<String>),
/// ADMIN [<target>] /// ADMIN [target]
ADMIN(Option<String>), ADMIN(Option<String>),
/// INFO [<target>] /// INFO [target]
INFO(Option<String>), INFO(Option<String>),
// 3.5 Service Query and Commands // 3.5 Service Query and Commands
/// SERVLIST [<mask> [<type>]] /// SERVLIST [mask [type]]
SERVLIST(Option<String>, Option<String>), SERVLIST(Option<String>, Option<String>),
/// SQUERY <servicename> <text> /// SQUERY servicename text
SQUERY(String, String), SQUERY(String, String),
// 3.6 User based queries // 3.6 User based queries
/// WHO [<mask> ["o"]] /// WHO [mask ["o"]]
WHO(Option<String>, Option<bool>), WHO(Option<String>, Option<bool>),
/// WHOIS [<target>] <masklist> /// WHOIS [target] masklist
WHOIS(Option<String>, String), WHOIS(Option<String>, String),
/// WHOWAS <nicklist> [<count> [<target>]] /// WHOWAS nicklist [count [target]]
WHOWAS(String, Option<String>, Option<String>), WHOWAS(String, Option<String>, Option<String>),
// 3.7 Miscellaneous messages // 3.7 Miscellaneous messages
/// KILL <nickname> <comment> /// KILL nickname comment
KILL(String, String), KILL(String, String),
/// PING <server1> [<server2>] /// PING server1 [server2]
PING(String, Option<String>), PING(String, Option<String>),
/// PONG <server> [<server2>] /// PONG server [server2]
PONG(String, Option<String>), PONG(String, Option<String>),
/// ERROR <error message> /// ERROR error message
ERROR(String), ERROR(String),
// 4 Optional Features // 4 Optional Features
/// AWAY [<text>] /// AWAY [text]
AWAY(Option<String>), AWAY(Option<String>),
/// REHASH /// REHASH
REHASH, REHASH,
@ -103,31 +106,33 @@ pub enum Command {
DIE, DIE,
/// RESTART /// RESTART
RESTART, RESTART,
/// SUMMON <user> [<target> [<channel>]] /// SUMMON user [target [channel]]
SUMMON(String, Option<String>, Option<String>), SUMMON(String, Option<String>, Option<String>),
/// USERS [<target>] /// USERS [target]
USERS(Option<String>), USERS(Option<String>),
/// WALLOPS <Text to be sent> /// WALLOPS Text to be sent
WALLOPS(String), WALLOPS(String),
/// USERHOST <space-separated nicklist> /// USERHOST space-separated nicklist
USERHOST(Vec<String>), USERHOST(Vec<String>),
/// ISON <space-separated nicklist> /// ISON space-separated nicklist
ISON(Vec<String>), ISON(Vec<String>),
// Non-RFC commands from InspIRCd // Non-RFC commands from InspIRCd
/// SAJOIN <nickname> <channel> /// SAJOIN nickname channel
SAJOIN(String, String), SAJOIN(String, String),
/// SAMODE <target> <modes> [<modeparams>] /// SAMODE target modes [modeparams]
SAMODE(String, String, Option<String>), SAMODE(String, String, Option<String>),
/// SANICK <old nickname> <new nickname> /// SANICK old nickname new nickname
SANICK(String, String), SANICK(String, String),
/// SAPART <nickname> <reason> /// SAPART nickname reason
SAPART(String, String), SAPART(String, String),
/// SAQUIT <nickname> <reason> /// SAQUIT nickname reason
SAQUIT(String, String), SAQUIT(String, String),
} }
impl Command { impl Command {
/// Converts a Command into a Message
#[stable]
pub fn to_message(self) -> Message { pub fn to_message(self) -> Message {
match self { match self {
PASS(p) => Message::new(None, "PASS", None, Some(p[])), PASS(p) => Message::new(None, "PASS", None, Some(p[])),
@ -221,6 +226,8 @@ impl Command {
} }
} }
/// Converts a Message into a Command
#[stable]
pub fn from_message(m: Message) -> IoResult<Command> { pub fn from_message(m: Message) -> IoResult<Command> {
Ok(if let "PASS" = m.command[] { Ok(if let "PASS" = m.command[] {
if m.suffix.is_some() { if m.suffix.is_some() {
@ -738,6 +745,8 @@ impl Command {
} }
} }
/// Produces an invalid_input IoError
#[stable]
fn invalid_input() -> IoError { fn invalid_input() -> IoError {
IoError { IoError {
kind: InvalidInput, kind: InvalidInput,

View file

@ -1,22 +1,37 @@
//! JSON configuration files using libserialize
#![stable]
use std::collections::HashMap; use std::collections::HashMap;
use std::io::fs::File; use std::io::fs::File;
use std::io::{InvalidInput, IoError, IoResult}; use std::io::{InvalidInput, IoError, IoResult};
use serialize::json::decode; use serialize::json::decode;
/// Configuration data
#[deriving(Clone, Decodable)] #[deriving(Clone, Decodable)]
#[unstable]
pub struct Config { pub struct Config {
/// A list of the owners of the bot by nickname
pub owners: Vec<String>, pub owners: Vec<String>,
/// The bot's nickname
pub nickname: String, pub nickname: String,
/// The bot's username
pub username: String, pub username: String,
/// The bot's real name
pub realname: String, pub realname: String,
/// The bot's password
pub password: String, pub password: String,
/// The server to connect to
pub server: String, pub server: String,
/// The port to connect on
pub port: u16, pub port: u16,
/// A list of channels to join on connection
pub channels: Vec<String>, pub channels: Vec<String>,
/// A map of additional options to be stored in config
pub options: HashMap<String, String>, pub options: HashMap<String, String>,
} }
impl Config { impl Config {
/// Loads a JSON configuration from the desired path.
#[stable]
pub fn load(path: Path) -> IoResult<Config> { pub fn load(path: Path) -> IoResult<Config> {
let mut file = try!(File::open(&path)); let mut file = try!(File::open(&path));
let data = try!(file.read_to_string()); let data = try!(file.read_to_string());
@ -27,10 +42,14 @@ impl Config {
}) })
} }
/// Loads a JSON configuration using the string as a UTF-8 path.
#[stable]
pub fn load_utf8(path: &str) -> IoResult<Config> { pub fn load_utf8(path: &str) -> IoResult<Config> {
Config::load(Path::new(path)) Config::load(Path::new(path))
} }
/// Determines whether or not the nickname provided is the owner of the bot.
#[stable]
pub fn is_owner(&self, nickname: &str) -> bool { pub fn is_owner(&self, nickname: &str) -> bool {
self.owners[].contains(&String::from_str(nickname)) self.owners[].contains(&String::from_str(nickname))
} }

View file

@ -1,14 +1,25 @@
//! Messages to and from the server
#![experimental]
use std::from_str::FromStr; use std::from_str::FromStr;
/// IRC Message data
#[experimental]
#[deriving(Clone, PartialEq, Show)] #[deriving(Clone, PartialEq, Show)]
pub struct Message { pub struct Message {
/// The message prefix (or source) as defined by [RFC 2812](http://tools.ietf.org/html/rfc2812)
pub prefix: Option<String>, pub prefix: Option<String>,
/// The IRC command as defined by [RFC 2812](http://tools.ietf.org/html/rfc2812)
pub command: String, pub command: String,
/// The command arguments
pub args: Vec<String>, pub args: Vec<String>,
/// The message suffix as defined by [RFC 2812](http://tools.ietf.org/html/rfc2812)
/// This is the only part of the message that is allowed to contain spaces.
pub suffix: Option<String>, pub suffix: Option<String>,
} }
impl Message { impl Message {
/// Creates a new Message
#[experimental]
pub fn new(prefix: Option<&str>, command: &str, args: Option<Vec<&str>>, suffix: Option<&str>) pub fn new(prefix: Option<&str>, command: &str, args: Option<Vec<&str>>, suffix: Option<&str>)
-> Message { -> Message {
Message { Message {
@ -19,6 +30,8 @@ impl Message {
} }
} }
/// Converts a Message into a String according to the IRC protocol
#[experimental]
pub fn into_string(&self) -> String { pub fn into_string(&self) -> String {
let mut ret = String::new(); let mut ret = String::new();
if let Some(ref prefix) = self.prefix { if let Some(ref prefix) = self.prefix {

View file

@ -1,6 +1,16 @@
//! Data related to IRC functionality
#![experimental]
pub mod kinds { pub mod kinds {
//! Trait definitions of appropriate Writers and Buffers for use with this library
#![unstable]
/// Trait describing all possible Writers for this library
#[unstable]
pub trait IrcWriter: Writer + Sized + Send + 'static {} pub trait IrcWriter: Writer + Sized + Send + 'static {}
impl<T> IrcWriter for T where T: Writer + Sized + Send + 'static {} impl<T> IrcWriter for T where T: Writer + Sized + Send + 'static {}
/// Trait describing all possible Readers for this library
#[unstable]
pub trait IrcReader: Buffer + Sized + Send + 'static {} pub trait IrcReader: Buffer + Sized + Send + 'static {}
impl<T> IrcReader for T where T: Buffer + Sized + Send + 'static {} impl<T> IrcReader for T where T: Buffer + Sized + Send + 'static {}
} }

View file

@ -1,6 +1,7 @@
//! A simple, thread-safe IRC client library. //! A simple, thread-safe IRC client library.
#![crate_name = "irc"] #![crate_name = "irc"]
#![crate_type = "lib"] #![crate_type = "lib"]
#![unstable]
#![feature(if_let)] #![feature(if_let)]
#![feature(slicing_syntax)] #![feature(slicing_syntax)]

View file

@ -1,3 +1,5 @@
//! Interface for working with IRC Servers
#![experimental]
use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream}; use std::io::{BufferedReader, BufferedWriter, IoResult, TcpStream};
use conn::Connection; use conn::Connection;
use data::command::Command; use data::command::Command;
@ -5,18 +7,30 @@ use data::config::Config;
use data::kinds::{IrcReader, IrcWriter}; use data::kinds::{IrcReader, IrcWriter};
use data::message::Message; use data::message::Message;
/// Trait describing core Server functionality
#[experimental]
pub trait Server<'a, T, U> { pub trait Server<'a, T, U> {
/// Gets the configuration being used with this Server
fn config(&self) -> &Config;
/// Sends a Command to this Server
fn send(&self, _: Command) -> IoResult<()>; fn send(&self, _: Command) -> IoResult<()>;
/// Gets an Iterator over Messages received by this Server
fn iter(&'a self) -> ServerIterator<'a, T, U>; fn iter(&'a self) -> ServerIterator<'a, T, U>;
} }
/// A thread-safe implementation of an IRC Server connection
#[experimental]
pub struct IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { pub struct IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader {
pub conn: Connection<T, U>, /// The thread-safe IRC connection
pub config: Config conn: Connection<T, U>,
/// The configuration used with this connection
config: Config
} }
impl<'a> IrcServer<'a, BufferedWriter<TcpStream>, BufferedReader<TcpStream>> { impl<'a> IrcServer<'a, BufferedWriter<TcpStream>, BufferedReader<TcpStream>> {
pub fn new(config: &str) -> IoResult<IrcServer<'a, BufferedWriter<TcpStream>, BufferedReader<TcpStream>>> { /// Creates a new IRC Server connection from the specified configuration, connecting immediately.
#[experimental]
pub fn new(config: &str) -> IoResult<IrcServer<'a, BufferedWriter<TcpStream>, BufferedReader<TcpStream>>> {
let config = try!(Config::load_utf8(config)); let config = try!(Config::load_utf8(config));
let conn = try!(Connection::connect(config.server[], config.port)); let conn = try!(Connection::connect(config.server[], config.port));
Ok(IrcServer { Ok(IrcServer {
@ -26,7 +40,11 @@ impl<'a> IrcServer<'a, BufferedWriter<TcpStream>, BufferedReader<TcpStream>> {
} }
} }
impl<'a, T, U> Server<'a, T, U> for IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader{ impl<'a, T, U> Server<'a, T, U> for IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader {
fn config(&self) -> &Config {
&self.config
}
fn send(&self, command: Command) -> IoResult<()> { fn send(&self, command: Command) -> IoResult<()> {
self.conn.send(command.to_message()) self.conn.send(command.to_message())
} }
@ -37,6 +55,8 @@ impl<'a, T, U> Server<'a, T, U> for IrcServer<'a, T, U> where T: IrcWriter, U: I
} }
impl<'a, T, U> IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader { impl<'a, T, U> IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader {
/// Creates an IRC server from the specified configuration, and any arbitrary Connection
#[experimental]
pub fn from_connection(config: &str, conn: Connection<T, U>) -> IoResult<IrcServer<'a, T, U>> { pub fn from_connection(config: &str, conn: Connection<T, U>) -> IoResult<IrcServer<'a, T, U>> {
Ok(IrcServer { Ok(IrcServer {
conn: conn, conn: conn,
@ -45,11 +65,15 @@ impl<'a, T, U> IrcServer<'a, T, U> where T: IrcWriter, U: IrcReader {
} }
} }
/// An Iterator over an IrcServer's incoming Messages
#[experimental]
pub struct ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader { pub struct ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader {
pub server: &'a IrcServer<'a, T, U> pub server: &'a IrcServer<'a, T, U>
} }
impl<'a, T, U> ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader { impl<'a, T, U> ServerIterator<'a, T, U> where T: IrcWriter, U: IrcReader {
/// Creates a new ServerIterator for the desired IrcServer
#[experimental]
pub fn new(server: &'a IrcServer<'a, T, U>) -> ServerIterator<'a, T, U> { pub fn new(server: &'a IrcServer<'a, T, U>) -> ServerIterator<'a, T, U> {
ServerIterator { ServerIterator {
server: server server: server