Completed channel user list tracking.
This commit is contained in:
parent
4e40fd8218
commit
dd6b6eebd3
3 changed files with 153 additions and 10 deletions
|
@ -2,11 +2,13 @@
|
|||
use std::from_str::FromStr;
|
||||
|
||||
/// IRC User data.
|
||||
#[deriving(PartialEq, Clone, Show)]
|
||||
#[deriving(Clone, Show)]
|
||||
pub struct User {
|
||||
/// The user's nickname.
|
||||
name: String,
|
||||
/// The user's access level.
|
||||
/// For simplicity, this is not used to determine the equality of two users.
|
||||
/// That is a user is equal if and only if their nickname is the same.
|
||||
access_level: AccessLevel,
|
||||
}
|
||||
|
||||
|
@ -23,9 +25,37 @@ impl User {
|
|||
access_level: rank.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the user's access level.
|
||||
pub fn access_level(&self) -> AccessLevel {
|
||||
self.access_level
|
||||
}
|
||||
|
||||
/// Updates the user's access level.
|
||||
pub fn update_access_level(&mut self, mode: &str) {
|
||||
self.access_level = match mode {
|
||||
"+q" => Owner,
|
||||
"-q" => Member,
|
||||
"+a" => Admin,
|
||||
"-a" => Member,
|
||||
"+o" => Oper,
|
||||
"-o" => Member,
|
||||
"+h" => HalfOp,
|
||||
"-h" => Member,
|
||||
"+v" => Voice,
|
||||
"-v" => Member,
|
||||
_ => self.access_level,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
impl PartialEq for User {
|
||||
fn eq(&self, other: &User) -> bool {
|
||||
self.name == other.name
|
||||
}
|
||||
}
|
||||
|
||||
/// The user's access level.
|
||||
#[deriving(PartialEq, Clone, Show)]
|
||||
pub enum AccessLevel {
|
||||
/// The channel owner (~).
|
||||
|
@ -44,7 +74,7 @@ pub enum AccessLevel {
|
|||
|
||||
impl FromStr for AccessLevel {
|
||||
fn from_str(s: &str) -> Option<AccessLevel> {
|
||||
if s.len() == 0 { None } else {
|
||||
if s.len() == 0 { Some(Member) } else {
|
||||
Some(match s.char_at(0) {
|
||||
'~' => Owner,
|
||||
'&' => Admin,
|
||||
|
@ -59,5 +89,53 @@ impl FromStr for AccessLevel {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{AccessLevel, Admin, HalfOp, Member, Oper, Owner, User, Voice};
|
||||
|
||||
#[test]
|
||||
fn access_level_from_str() {
|
||||
assert_eq!(from_str::<AccessLevel>("member").unwrap(), Member);
|
||||
assert_eq!(from_str::<AccessLevel>("~owner").unwrap(), Owner);
|
||||
assert_eq!(from_str::<AccessLevel>("&admin").unwrap(), Admin);
|
||||
assert_eq!(from_str::<AccessLevel>("@oper").unwrap(), Oper);
|
||||
assert_eq!(from_str::<AccessLevel>("%halfop").unwrap(), HalfOp);
|
||||
assert_eq!(from_str::<AccessLevel>("+voice").unwrap(), Voice);
|
||||
assert_eq!(from_str::<AccessLevel>("").unwrap(), Member);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_user() {
|
||||
let user = User::new("~owner");
|
||||
let exp = User {
|
||||
name: format!("owner"),
|
||||
access_level: Owner,
|
||||
};
|
||||
assert_eq!(user, exp);
|
||||
assert_eq!(user.access_level, exp.access_level);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_user_rank() {
|
||||
let mut user = User::new("user");
|
||||
assert_eq!(user.access_level, Member);
|
||||
user.update_access_level("+q");
|
||||
assert_eq!(user.access_level, Owner);
|
||||
user.update_access_level("-q");
|
||||
assert_eq!(user.access_level, Member);
|
||||
user.update_access_level("+a");
|
||||
assert_eq!(user.access_level, Admin);
|
||||
user.update_access_level("-a");
|
||||
assert_eq!(user.access_level, Member);
|
||||
user.update_access_level("+o");
|
||||
assert_eq!(user.access_level, Oper);
|
||||
user.update_access_level("-o");
|
||||
assert_eq!(user.access_level, Member);
|
||||
user.update_access_level("+h");
|
||||
assert_eq!(user.access_level, HalfOp);
|
||||
user.update_access_level("-h");
|
||||
assert_eq!(user.access_level, Member);
|
||||
user.update_access_level("+v");
|
||||
assert_eq!(user.access_level, Voice);
|
||||
user.update_access_level("-v");
|
||||
assert_eq!(user.access_level, Member);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ pub trait Server<'a, T> {
|
|||
fn send(&self, _: Command) -> IoResult<()>;
|
||||
/// Gets an Iterator over Messages received by this Server.
|
||||
fn iter(&'a self) -> ServerIterator<'a, T>;
|
||||
/// Gets a list of Users in the specified channel.
|
||||
fn list_users(&self, _: &str) -> Option<Vec<User>>;
|
||||
}
|
||||
|
||||
/// A thread-safe implementation of an IRC Server connection.
|
||||
|
@ -71,6 +73,10 @@ impl<'a, T> Server<'a, T> for IrcServer<'a, T> where T: IrcStream {
|
|||
fn iter(&'a self) -> ServerIterator<'a, T> {
|
||||
ServerIterator::new(self)
|
||||
}
|
||||
|
||||
fn list_users(&self, chan: &str) -> Option<Vec<User>> {
|
||||
self.chanlists.lock().find_copy(&chan.into_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> IrcServer<'a, T> where T: IrcStream {
|
||||
|
@ -94,7 +100,7 @@ impl<'a, T> IrcServer<'a, T> where T: IrcStream {
|
|||
for chan in self.config.channels.iter() {
|
||||
self.send(JOIN(chan[], None)).unwrap();
|
||||
}
|
||||
} /* FIXME: it's not really clear why this stuff is broken.
|
||||
}
|
||||
else if message.command[] == "353" { // /NAMES
|
||||
if let Some(users) = message.suffix.clone() {
|
||||
if let [_, _, ref chan] = message.args[] {
|
||||
|
@ -114,7 +120,7 @@ impl<'a, T> IrcServer<'a, T> where T: IrcStream {
|
|||
None => message.args[0][],
|
||||
};
|
||||
if let Some(vec) = self.chanlists.lock().get_mut(&String::from_str(chan)) {
|
||||
if let Some(ref source) = message.suffix {
|
||||
if let Some(ref source) = message.prefix {
|
||||
if let Some(i) = source.find('!') {
|
||||
if message.command[] == "JOIN" {
|
||||
vec.push(User::new(source[..i]));
|
||||
|
@ -126,8 +132,13 @@ impl<'a, T> IrcServer<'a, T> where T: IrcStream {
|
|||
}
|
||||
}
|
||||
}
|
||||
} */
|
||||
/* TODO: implement more message handling */
|
||||
} else if let ("MODE", [ref chan, ref mode, ref user]) = (message.command[], message.args[]) {
|
||||
if let Some(vec) = self.chanlists.lock().get_mut(chan) {
|
||||
if let Some(n) = vec.as_slice().position_elem(&User::new(user[])) {
|
||||
vec[n].update_access_level(mode[]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,7 +179,7 @@ mod test {
|
|||
use std::io::{MemReader, MemWriter};
|
||||
use std::io::util::{NullReader, NullWriter};
|
||||
use conn::{Connection, IoStream};
|
||||
use data::Config;
|
||||
use data::{Config, User};
|
||||
use data::command::PRIVMSG;
|
||||
use data::kinds::IrcReader;
|
||||
|
||||
|
@ -223,4 +234,54 @@ mod test {
|
|||
assert_eq!(get_server_value(server)[],
|
||||
"PRIVMSG #test :Hi there!\r\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_tracking_names() {
|
||||
let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n";
|
||||
let server = IrcServer::from_connection(test_config(),
|
||||
Connection::new(IoStream::new(NullWriter, MemReader::new(value.as_bytes().to_vec()))));
|
||||
for message in server.iter() {
|
||||
println!("{}", message);
|
||||
}
|
||||
assert_eq!(server.list_users("#test").unwrap(),
|
||||
vec![User::new("test"), User::new("~owner"), User::new("&admin")])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_tracking_names_join() {
|
||||
let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n:test2!test@test JOIN #test\r\n";
|
||||
let server = IrcServer::from_connection(test_config(),
|
||||
Connection::new(IoStream::new(NullWriter, MemReader::new(value.as_bytes().to_vec()))));
|
||||
for message in server.iter() {
|
||||
println!("{}", message);
|
||||
}
|
||||
assert_eq!(server.list_users("#test").unwrap(),
|
||||
vec![User::new("test"), User::new("~owner"), User::new("&admin"), User::new("test2")])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_tracking_names_part() {
|
||||
let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n:owner!test@test PART #test\r\n";
|
||||
let server = IrcServer::from_connection(test_config(),
|
||||
Connection::new(IoStream::new(NullWriter, MemReader::new(value.as_bytes().to_vec()))));
|
||||
for message in server.iter() {
|
||||
println!("{}", message);
|
||||
}
|
||||
assert_eq!(server.list_users("#test").unwrap(),
|
||||
vec![User::new("test"), User::new("&admin")])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_tracking_names_mode() {
|
||||
let value = ":irc.test.net 353 test = #test :test ~owner &admin\r\n:test!test@test MODE #test +o test\r\n";
|
||||
let server = IrcServer::from_connection(test_config(),
|
||||
Connection::new(IoStream::new(NullWriter, MemReader::new(value.as_bytes().to_vec()))));
|
||||
for message in server.iter() {
|
||||
println!("{}", message);
|
||||
}
|
||||
assert_eq!(server.list_users("#test").unwrap(),
|
||||
vec![User::new("@test"), User::new("~owner"), User::new("&admin")]);
|
||||
assert_eq!(server.list_users("#test").unwrap()[0].access_level(),
|
||||
User::new("@test").access_level());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
#![experimental]
|
||||
|
||||
use std::io::IoResult;
|
||||
use data::command::{Command, INVITE, JOIN, KILL, MODE, NICK, KICK};
|
||||
use data::{Command, Config, User};
|
||||
use data::command::{INVITE, JOIN, KILL, MODE, NICK, KICK};
|
||||
use data::command::{OPER, PONG, PRIVMSG, SAMODE, SANICK, TOPIC, USER};
|
||||
use data::config::Config;
|
||||
use data::kinds::IrcStream;
|
||||
use server::{Server, ServerIterator};
|
||||
|
||||
|
@ -26,6 +26,10 @@ impl<'a, T> Server<'a, T> for Wrapper<'a, T> where T: IrcStream {
|
|||
fn iter(&'a self) -> ServerIterator<'a, T> {
|
||||
self.server.iter()
|
||||
}
|
||||
|
||||
fn list_users(&self, chan: &str) -> Option<Vec<User>> {
|
||||
self.server.list_users(chan)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Wrapper<'a, T> where T: IrcStream {
|
||||
|
|
Loading…
Add table
Reference in a new issue