Implemented #4 as a list of alternate nicknames.
This commit is contained in:
parent
67bfcee53c
commit
a222989cb4
3 changed files with 61 additions and 8 deletions
|
@ -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"
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in a new issue