use std::fmt;
use crate::proto::Command;
use crate::proto::MessageParseError::{self, *};
use crate::proto::ModeParseError::*;
pub trait ModeType: fmt::Display + fmt::Debug + Clone + PartialEq {
fn mode(target: &str, modes: &[Mode<Self>]) -> Command;
fn takes_arg(&self) -> bool;
}
#[derive(Clone, Debug, PartialEq)]
pub enum UserMode {
Away,
Invisible,
Wallops,
Restricted,
Oper,
LocalOper,
ServerNotices,
MaskedHost,
Unknown(char),
}
impl ModeType for UserMode {
fn mode(target: &str, modes: &[Mode<Self>]) -> Command {
Command::UserMODE(target.to_owned(), modes.to_owned())
}
fn takes_arg(&self) -> bool {
false
}
}
impl UserMode {
fn from_char(c: char) -> UserMode {
use self::UserMode::*;
match c {
'a' => Away,
'i' => Invisible,
'w' => Wallops,
'r' => Restricted,
'o' => Oper,
'O' => LocalOper,
's' => ServerNotices,
'x' => MaskedHost,
_ => Unknown(c),
}
}
}
impl fmt::Display for UserMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::UserMode::*;
write!(
f,
"{}",
match *self {
Away => 'a',
Invisible => 'i',
Wallops => 'w',
Restricted => 'r',
Oper => 'o',
LocalOper => 'O',
ServerNotices => 's',
MaskedHost => 'x',
Unknown(c) => c,
}
)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ChannelMode {
Ban,
Exception,
Limit,
InviteOnly,
InviteException,
Key,
Moderated,
RegisteredOnly,
Secret,
ProtectedTopic,
NoExternalMessages,
Founder,
Admin,
Oper,
Halfop,
Voice,
Unknown(char),
}
impl ModeType for ChannelMode {
fn mode(target: &str, modes: &[Mode<Self>]) -> Command {
Command::ChannelMODE(target.to_owned(), modes.to_owned())
}
fn takes_arg(&self) -> bool {
use self::ChannelMode::*;
match *self {
Ban | Exception | Limit | InviteException | Key | Founder | Admin | Oper | Halfop
| Voice => true,
_ => false,
}
}
}
impl ChannelMode {
fn from_char(c: char) -> ChannelMode {
use self::ChannelMode::*;
match c {
'b' => Ban,
'e' => Exception,
'l' => Limit,
'i' => InviteOnly,
'I' => InviteException,
'k' => Key,
'm' => Moderated,
'r' => RegisteredOnly,
's' => Secret,
't' => ProtectedTopic,
'n' => NoExternalMessages,
'q' => Founder,
'a' => Admin,
'o' => Oper,
'h' => Halfop,
'v' => Voice,
_ => Unknown(c),
}
}
}
impl fmt::Display for ChannelMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::ChannelMode::*;
write!(
f,
"{}",
match *self {
Ban => 'b',
Exception => 'e',
Limit => 'l',
InviteOnly => 'i',
InviteException => 'I',
Key => 'k',
Moderated => 'm',
RegisteredOnly => 'r',
Secret => 's',
ProtectedTopic => 't',
NoExternalMessages => 'n',
Founder => 'q',
Admin => 'a',
Oper => 'o',
Halfop => 'h',
Voice => 'v',
Unknown(c) => c,
}
)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Mode<T>
where
T: ModeType,
{
Plus(T, Option<String>),
Minus(T, Option<String>),
}
impl<T> Mode<T>
where
T: ModeType,
{
pub fn plus(inner: T, arg: Option<&str>) -> Mode<T> {
Mode::Plus(inner, arg.map(|s| s.to_owned()))
}
pub fn minus(inner: T, arg: Option<&str>) -> Mode<T> {
Mode::Minus(inner, arg.map(|s| s.to_owned()))
}
}
impl<T> fmt::Display for Mode<T>
where
T: ModeType,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Mode::Plus(ref mode, Some(ref arg)) => write!(f, "+{} {}", mode, arg),
Mode::Minus(ref mode, Some(ref arg)) => write!(f, "+{} {}", mode, arg),
Mode::Plus(ref mode, None) => write!(f, "+{}", mode),
Mode::Minus(ref mode, None) => write!(f, "-{}", mode),
}
}
}
enum PlusMinus {
Plus,
Minus,
}
impl Mode<UserMode> {
pub fn from_user_mode_string(s: &str) -> Result<Vec<Mode<UserMode>>, MessageParseError> {
use self::PlusMinus::*;
let mut res = vec![];
let mut pieces = s.split(' ');
for term in pieces.clone() {
if term.starts_with('+') || term.starts_with('-') {
let _ = pieces.next();
let mut chars = term.chars();
let init = match chars.next() {
Some('+') => Plus,
Some('-') => Minus,
Some(c) => {
return Err(InvalidModeString {
string: s.to_owned(),
cause: InvalidModeModifier { modifier: c },
})
}
None => {
return Err(InvalidModeString {
string: s.to_owned(),
cause: MissingModeModifier,
})
}
};
for c in chars {
let mode = UserMode::from_char(c);
let arg = if mode.takes_arg() {
pieces.next()
} else {
None
};
res.push(match init {
Plus => Mode::Plus(mode, arg.map(|s| s.to_owned())),
Minus => Mode::Minus(mode, arg.map(|s| s.to_owned())),
})
}
}
}
Ok(res)
}
}
impl Mode<ChannelMode> {
pub fn from_channel_mode_string(s: &str) -> Result<Vec<Mode<ChannelMode>>, MessageParseError> {
use self::PlusMinus::*;
let mut res = vec![];
let mut pieces = s.split(' ');
for term in pieces.clone() {
if term.starts_with('+') || term.starts_with('-') {
let _ = pieces.next();
let mut chars = term.chars();
let init = match chars.next() {
Some('+') => Plus,
Some('-') => Minus,
Some(c) => {
return Err(InvalidModeString {
string: s.to_owned(),
cause: InvalidModeModifier { modifier: c },
})
}
None => {
return Err(InvalidModeString {
string: s.to_owned(),
cause: MissingModeModifier,
})
}
};
for c in chars {
let mode = ChannelMode::from_char(c);
let arg = if mode.takes_arg() {
pieces.next()
} else {
None
};
res.push(match init {
Plus => Mode::Plus(mode, arg.map(|s| s.to_owned())),
Minus => Mode::Minus(mode, arg.map(|s| s.to_owned())),
})
}
}
}
Ok(res)
}
}