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;
|
use std::from_str::FromStr;
|
||||||
|
|
||||||
/// IRC User data.
|
/// IRC User data.
|
||||||
#[deriving(PartialEq, Clone, Show)]
|
#[deriving(Clone, Show)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
/// The user's nickname.
|
/// The user's nickname.
|
||||||
name: String,
|
name: String,
|
||||||
/// The user's access level.
|
/// 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,
|
access_level: AccessLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,9 +25,37 @@ impl User {
|
||||||
access_level: rank.unwrap(),
|
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)]
|
#[deriving(PartialEq, Clone, Show)]
|
||||||
pub enum AccessLevel {
|
pub enum AccessLevel {
|
||||||
/// The channel owner (~).
|
/// The channel owner (~).
|
||||||
|
@ -44,7 +74,7 @@ pub enum AccessLevel {
|
||||||
|
|
||||||
impl FromStr for AccessLevel {
|
impl FromStr for AccessLevel {
|
||||||
fn from_str(s: &str) -> Option<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) {
|
Some(match s.char_at(0) {
|
||||||
'~' => Owner,
|
'~' => Owner,
|
||||||
'&' => Admin,
|
'&' => Admin,
|
||||||
|
@ -59,5 +89,53 @@ impl FromStr for AccessLevel {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod 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<()>;
|
fn send(&self, _: Command) -> IoResult<()>;
|
||||||
/// Gets an Iterator over Messages received by this Server.
|
/// Gets an Iterator over Messages received by this Server.
|
||||||
fn iter(&'a self) -> ServerIterator<'a, T>;
|
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.
|
/// 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> {
|
fn iter(&'a self) -> ServerIterator<'a, T> {
|
||||||
ServerIterator::new(self)
|
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 {
|
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() {
|
for chan in self.config.channels.iter() {
|
||||||
self.send(JOIN(chan[], None)).unwrap();
|
self.send(JOIN(chan[], None)).unwrap();
|
||||||
}
|
}
|
||||||
} /* FIXME: it's not really clear why this stuff is broken.
|
}
|
||||||
else if message.command[] == "353" { // /NAMES
|
else if message.command[] == "353" { // /NAMES
|
||||||
if let Some(users) = message.suffix.clone() {
|
if let Some(users) = message.suffix.clone() {
|
||||||
if let [_, _, ref chan] = message.args[] {
|
if let [_, _, ref chan] = message.args[] {
|
||||||
|
@ -114,7 +120,7 @@ impl<'a, T> IrcServer<'a, T> where T: IrcStream {
|
||||||
None => message.args[0][],
|
None => message.args[0][],
|
||||||
};
|
};
|
||||||
if let Some(vec) = self.chanlists.lock().get_mut(&String::from_str(chan)) {
|
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 let Some(i) = source.find('!') {
|
||||||
if message.command[] == "JOIN" {
|
if message.command[] == "JOIN" {
|
||||||
vec.push(User::new(source[..i]));
|
vec.push(User::new(source[..i]));
|
||||||
|
@ -126,8 +132,13 @@ impl<'a, T> IrcServer<'a, T> where T: IrcStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} */
|
} else if let ("MODE", [ref chan, ref mode, ref user]) = (message.command[], message.args[]) {
|
||||||
/* TODO: implement more message handling */
|
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::{MemReader, MemWriter};
|
||||||
use std::io::util::{NullReader, NullWriter};
|
use std::io::util::{NullReader, NullWriter};
|
||||||
use conn::{Connection, IoStream};
|
use conn::{Connection, IoStream};
|
||||||
use data::Config;
|
use data::{Config, User};
|
||||||
use data::command::PRIVMSG;
|
use data::command::PRIVMSG;
|
||||||
use data::kinds::IrcReader;
|
use data::kinds::IrcReader;
|
||||||
|
|
||||||
|
@ -223,4 +234,54 @@ mod test {
|
||||||
assert_eq!(get_server_value(server)[],
|
assert_eq!(get_server_value(server)[],
|
||||||
"PRIVMSG #test :Hi there!\r\n");
|
"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]
|
#![experimental]
|
||||||
|
|
||||||
use std::io::IoResult;
|
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::command::{OPER, PONG, PRIVMSG, SAMODE, SANICK, TOPIC, USER};
|
||||||
use data::config::Config;
|
|
||||||
use data::kinds::IrcStream;
|
use data::kinds::IrcStream;
|
||||||
use server::{Server, ServerIterator};
|
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> {
|
fn iter(&'a self) -> ServerIterator<'a, T> {
|
||||||
self.server.iter()
|
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 {
|
impl<'a, T> Wrapper<'a, T> where T: IrcStream {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue