Implemented #4 as a list of alternate nicknames.

This commit is contained in:
Aaron Weiss 2014-12-06 18:49:07 -05:00
parent 67bfcee53c
commit a222989cb4
3 changed files with 61 additions and 8 deletions

View file

@ -1,7 +1,7 @@
[package]
name = "irc"
version = "0.5.0"
version = "0.5.1"
description = "A simple, thread-safe IRC client library."
authors = ["Aaron Weiss <aaronweiss74@gmail.com>"]
license = "Unlicense"

View file

@ -13,6 +13,8 @@ pub struct Config {
pub owners: Option<Vec<String>>,
/// The bot's nickname.
pub nickname: Option<String>,
/// Alternative nicknames for the bots, if the default is taken.
pub alt_nicks: Option<Vec<String>>,
/// The bot's username.
pub username: Option<String>,
/// The bot's real name.
@ -61,11 +63,20 @@ impl Config {
}
/// Gets the nickname specified in the configuration.
/// This will panic if not specified.
#[experimental]
pub fn nickname(&self) -> &str {
self.nickname.as_ref().map(|s| s[]).unwrap()
}
/// Gets the alternate nicknames specified in the configuration.
/// This defaults to an empty vector when not specified.
#[experimental]
pub fn get_alternate_nicknames(&self) -> Vec<&str> {
self.alt_nicks.as_ref().map(|v| v.iter().map(|s| s[]).collect()).unwrap_or(vec![])
}
/// Gets the username specified in the configuration.
/// This defaults to the user's nickname when not specified.
#[experimental]
@ -146,6 +157,7 @@ mod test {
let cfg = Config {
owners: Some(vec![format!("test")]),
nickname: Some(format!("test")),
alt_nicks: None,
username: Some(format!("test")),
realname: Some(format!("test")),
password: Some(String::new()),
@ -164,6 +176,7 @@ mod test {
let cfg = Config {
owners: Some(vec![format!("test")]),
nickname: Some(format!("test")),
alt_nicks: None,
username: Some(format!("test")),
realname: Some(format!("test")),
password: Some(String::new()),

View file

@ -2,10 +2,10 @@
#![experimental]
use std::collections::HashMap;
use std::io::{BufferedReader, BufferedWriter, IoResult};
use std::sync::Mutex;
use std::sync::{Mutex, RWLock};
use conn::{Connection, NetStream};
use data::{Command, Config, Message, Response, User};
use data::Command::{JOIN, PONG};
use data::Command::{JOIN, NICK, PONG};
use data::kinds::{IrcReader, IrcWriter};
pub mod utils;
@ -32,6 +32,8 @@ pub struct IrcServer<T: IrcReader, U: IrcWriter> {
config: Config,
/// A thread-safe map of channels to the list of users in them.
chanlists: Mutex<HashMap<String, Vec<User>>>,
/// A thread-safe index to track the current alternative nickname being used.
alt_nick_index: RWLock<uint>,
}
/// An IrcServer over a buffered NetStream.
@ -61,7 +63,8 @@ impl IrcServer<BufferedReader<NetStream>, BufferedWriter<NetStream>> {
} else {
Connection::connect(config.server(), config.port())
});
Ok(IrcServer { config: config, conn: conn, chanlists: Mutex::new(HashMap::new()) })
Ok(IrcServer { config: config, conn: conn, chanlists: Mutex::new(HashMap::new()),
alt_nick_index: RWLock::new(0u) })
}
/// Creates a new IRC server connection from the specified configuration with the specified
@ -74,7 +77,8 @@ impl IrcServer<BufferedReader<NetStream>, BufferedWriter<NetStream>> {
} else {
Connection::connect_with_timeout(config.server(), config.port(), timeout_ms)
});
Ok(IrcServer { config: config, conn: conn, chanlists: Mutex::new(HashMap::new()) })
Ok(IrcServer { config: config, conn: conn, chanlists: Mutex::new(HashMap::new()),
alt_nick_index: RWLock::new(0u) })
}
}
@ -107,7 +111,8 @@ impl<T: IrcReader, U: IrcWriter> IrcServer<T, U> {
/// Creates an IRC server from the specified configuration, and any arbitrary Connection.
#[experimental]
pub fn from_connection(config: Config, conn: Connection<T, U>) -> IrcServer<T, U> {
IrcServer { conn: conn, config: config, chanlists: Mutex::new(HashMap::new()) }
IrcServer { conn: conn, config: config, chanlists: Mutex::new(HashMap::new()),
alt_nick_index: RWLock::new(0u) }
}
/// Gets a reference to the IRC server's connection.
@ -136,6 +141,16 @@ impl<T: IrcReader, U: IrcWriter> IrcServer<T, U> {
for chan in self.config.channels().into_iter() {
self.send(JOIN(chan[], None)).unwrap();
}
} else if resp == Response::ERR_NICKNAMEINUSE ||
resp == Response::ERR_ERRONEOUSNICKNAME {
let alt_nicks = self.config.get_alternate_nicknames();
let mut index = self.alt_nick_index.write();
if *index.deref() >= alt_nicks.len() {
panic!("All specified nicknames were in use.")
} else {
self.send(NICK(alt_nicks[*index.deref()])).unwrap();
*index.deref_mut() += 1;
}
}
return
}
@ -225,6 +240,7 @@ mod test {
Config {
owners: Some(vec![format!("test")]),
nickname: Some(format!("test")),
alt_nicks: Some(vec![format!("test2")]),
server: Some(format!("irc.test.net")),
channels: Some(vec![format!("#test"), format!("#test2")]),
.. Default::default()
@ -263,14 +279,38 @@ mod test {
"PONG :irc.test.net\r\nJOIN #test\r\nJOIN #test2\r\n");
}
#[test]
fn nickname_in_use() {
let value = ":irc.pdgn.co 433 * test :Nickname is already in use.";
let server = IrcServer::from_connection(test_config(), Connection::new(
MemReader::new(value.as_bytes().to_vec()), MemWriter::new()
));
for message in server.iter() {
println!("{}", message);
}
assert_eq!(get_server_value(server)[], "NICK :test2\r\n");
}
#[test]
#[should_fail]
fn ran_out_of_nicknames() {
let value = ":irc.pdgn.co 433 * test :Nickname is already in use.\r\n\
:irc.pdgn.co 433 * test2 :Nickname is already in use.\r\n";
let server = IrcServer::from_connection(test_config(), Connection::new(
MemReader::new(value.as_bytes().to_vec()), MemWriter::new()
));
for message in server.iter() {
println!("{}", message);
}
}
#[test]
fn send() {
let server = IrcServer::from_connection(test_config(), Connection::new(
NullReader, MemWriter::new()
));
assert!(server.send(PRIVMSG("#test", "Hi there!")).is_ok());
assert_eq!(get_server_value(server)[],
"PRIVMSG #test :Hi there!\r\n");
assert_eq!(get_server_value(server)[], "PRIVMSG #test :Hi there!\r\n");
}
#[test]