use std::fmt;
use std::error::Error;
use const_table::const_table;
use crate::utils::*;
#[derive(Clone, Copy, Debug)]
pub(crate) enum MessageError {
Empty,
WrongSource,
NoCommand,
}
impl fmt::Display for MessageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MessageError::Empty => write!(f, "Message is empty"),
MessageError::WrongSource => write!(f, "Wrong source syntax"),
MessageError::NoCommand => write!(f, "No command"),
}
}
}
impl Error for MessageError {
}
#[derive(PartialEq, Eq, Debug)]
pub(crate) struct Message<'a> {
source: Option<&'a str>,
command: &'a str,
params: Vec<&'a str>,
}
impl<'a> Message<'a> {
pub(crate) fn from_shared_str(input: &'a str) -> Result<Self, MessageError> {
let trimmed = input.trim_start();
if !trimmed.is_empty() {
let start_pos = if trimmed.bytes().next() == Some(b':') { 1 } else { 0 };
let (rest, last_param) =
if let Some((rest, lp)) = trimmed[start_pos..].split_once(':') {
(&trimmed[0..rest.len() + start_pos], Some(lp))
} else {
(trimmed, None)
};
let mut rest_words = rest.split_ascii_whitespace();
let source = if rest.bytes().next() == Some(b':') {
let s = &rest_words.next().unwrap()[1..];
if !validate_source(s) {
return Err(MessageError::WrongSource);
}
Some(s) } else { None };
let command = if let Some(cmd) = rest_words.next() { cmd }
else { return Err(MessageError::NoCommand); };
let mut params = rest_words.collect::<Vec<_>>();
if let Some(lp) = last_param {
params.push(lp); }
Ok(Message{ source, command, params })
} else {
Err(MessageError::Empty)
}
}
pub(crate) fn to_string_with_source<'b>(&self, source: &'b str) -> String {
let mut out = ":".to_string();
out += source;
out.push(' ');
out += self.command;
if !self.params.is_empty() {
self.params[..self.params.len()-1].iter().for_each(|s| {
out.push(' ');
out += s;
});
let last = self.params[self.params.len()-1];
if last.find(|c| c==':' || c == ' ' || c == '\t').is_some() ||
last.is_empty() {
out += " :";
} else {
out.push(' ');
}
out += last;
}
out
}
}
#[const_table]
pub(crate) enum CommandId {
CommandName{ pub(crate) name: &'static str },
CAPId = CommandName{ name: "CAP" },
PASSId = CommandName{ name: "PASS" },
NICKId = CommandName{ name: "NICK" },
USERId = CommandName{ name: "USER" },
PINGId = CommandName{ name: "PING" },
PONGId = CommandName{ name: "PONG" },
OPERId = CommandName{ name: "OPER" },
JOINId = CommandName{ name: "JOIN" },
PARTId = CommandName{ name: "PART" },
TOPICId = CommandName{ name: "TOPIC" },
NAMESId = CommandName{ name: "NAMES" },
LISTId = CommandName{ name: "LIST" },
INVITEId = CommandName{ name: "INVITE" },
KICKId = CommandName{ name: "KICK" },
MOTDId = CommandName{ name: "MOTD" },
VERSIONId = CommandName{ name: "VERSION" },
ADMINId = CommandName{ name: "ADMIN" },
CONNECTId = CommandName{ name: "CONNECT" },
TIMEId = CommandName{ name: "TIME" },
STATSId = CommandName{ name: "STATS" },
LINKSId = CommandName{ name: "LINKS" },
MODEId = CommandName{ name: "MODE" },
PRIVMSGId = CommandName{ name: "PRIVMSG" },
NOTICEId = CommandName{ name: "NOTICE" },
WHOId = CommandName{ name: "WHO" },
WHOISId = CommandName{ name: "WHOIS" },
WHOWASId = CommandName{ name: "WHOWAS" },
KILLId = CommandName{ name: "KILL" },
SQUITId = CommandName{ name: "SQUIT" },
USERHOSTId = CommandName{ name: "USERHOST" },
WALLOPSId = CommandName{ name: "WALLOPS" },
}
use CommandId::*;
#[derive(Clone, Debug)]
pub(crate) enum CommandError {
UnknownCommand(String),
UnknownSubcommand(CommandId, String),
NeedMoreParams(CommandId),
ParameterDoesntMatch(CommandId, usize),
WrongParameter(CommandId, usize),
UnknownMode(usize, char, String),
UnknownUModeFlag(usize),
InvalidModeParam{ target: String, modechar: char, param: String, description: String },
}
use CommandError::*;
impl fmt::Display for CommandError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UnknownCommand(s) =>
write!(f, "Unknown command '{}'", s),
UnknownSubcommand(cmd, scmd) =>
write!(f, "Unknown subcommand '{}' in command '{}'", scmd, cmd.name),
NeedMoreParams(s) =>
write!(f, "Command '{}' needs more parameters", s.name),
ParameterDoesntMatch(s, i) =>
write!(f, "Parameter {} doesn't match for command '{}'", i, s.name),
WrongParameter(s, i) =>
write!(f, "Wrong parameter {} in command '{}'", i, s.name),
UnknownMode(i, c, ch) =>
write!(f, "Unknown mode {} in parameter {} for {}", c, i, ch),
UnknownUModeFlag(i) =>
write!(f, "Unknown umode flag in parameter {}", i),
InvalidModeParam{ modechar, param, target, description } =>
write!(f, "Invalid mode parameter: {} {} {} {}", target, modechar,
param, description),
}
}
}
impl Error for CommandError {
}
#[derive(PartialEq, Eq, Debug)]
pub(crate) enum CapCommand {
LS, LIST, REQ, END
}
#[derive(PartialEq, Eq, Debug)]
pub(crate) enum Command<'a> {
CAP{ subcommand: CapCommand, caps: Option<Vec<&'a str>>, version: Option<u32> },
AUTHENTICATE{ },
PASS{ password: &'a str },
NICK{ nickname: &'a str },
USER{ username: &'a str, hostname: &'a str, servername: &'a str, realname: &'a str },
PING{ token: &'a str },
PONG{ token: &'a str },
OPER{ name: &'a str, password: &'a str },
QUIT{ },
JOIN{ channels: Vec<&'a str>, keys: Option<Vec<&'a str>> },
PART{ channels: Vec<&'a str>, reason: Option<&'a str> },
TOPIC{ channel: &'a str, topic: Option<&'a str> },
NAMES{ channels: Vec<&'a str> },
LIST{ channels: Vec<&'a str>, server: Option<&'a str> },
INVITE{ nickname: &'a str, channel: &'a str },
KICK{ channel: &'a str, users: Vec<&'a str>, comment: Option<&'a str> },
MOTD{ target: Option<&'a str> },
VERSION{ target: Option<&'a str> },
ADMIN{ target: Option<&'a str> },
CONNECT{ target_server: &'a str, port: Option<u16>, remote_server: Option<&'a str> },
LUSERS{ },
TIME{ server: Option<&'a str> },
STATS{ query: char, server: Option<&'a str> },
LINKS{ remote_server: Option<&'a str>, server_mask: Option<&'a str> },
HELP{ subject: Option<&'a str> },
INFO{ },
MODE{ target: &'a str, modes: Vec<(&'a str, Vec<&'a str>)> },
PRIVMSG{ targets: Vec<&'a str>, text: &'a str },
NOTICE{ targets: Vec<&'a str>, text: &'a str },
WHO{ mask: &'a str },
WHOIS{ target: Option<&'a str>, nickmasks: Vec<&'a str> },
WHOWAS{ nickname: &'a str, count: Option<usize>, server: Option<&'a str> },
KILL{ nickname: &'a str, comment: &'a str },
REHASH{ },
RESTART{ },
SQUIT{ server: &'a str, comment: &'a str },
AWAY{ text: Option<&'a str> },
USERHOST{ nicknames: Vec<&'a str> },
WALLOPS{ text: &'a str },
}
use Command::*;
impl<'a> Command<'a> {
fn parse_from_message(message: &Message<'a>) -> Result<Self, CommandError> {
match message.command.to_ascii_uppercase().as_str() {
"CAP" => {
if !message.params.is_empty() {
let mut param_it = message.params.iter();
let subcommand = match param_it.next().unwrap()
.to_ascii_uppercase().as_str() {
"LS" => CapCommand::LS,
"LIST" => CapCommand::LIST,
"REQ" => CapCommand::REQ,
"END" => CapCommand::END,
_ => return Err(UnknownSubcommand(
CAPId, message.params[0].to_string()))
};
let (caps, version) = if subcommand == CapCommand::REQ {
(param_it.next().map(|x| x.split_ascii_whitespace().
collect::<Vec<_>>()),
None)
} else if subcommand == CapCommand::LS {
let v = if let Some(s) = param_it.next() {
if let Ok(value) = s.parse() { Some(value) }
else { return Err(WrongParameter(CAPId, 1)); }
} else { None };
(None, v)
} else { (None, None) };
Ok(CAP{ subcommand, caps, version })
} else {
Err(NeedMoreParams(CAPId)) }
},
"AUTHENTICATE" => Ok(AUTHENTICATE{}),
"PASS" => {
if !message.params.is_empty() {
Ok(PASS{ password: message.params[0] })
} else {
Err(NeedMoreParams(PASSId)) }
}
"NICK" => {
if !message.params.is_empty() {
Ok(NICK{ nickname: message.params[0] })
} else {
Err(NeedMoreParams(NICKId)) }
}
"USER" => {
if message.params.len() >= 4 {
Ok(USER{ username: message.params[0],
hostname: message.params[1],
servername: message.params[2],
realname: message.params[3] })
} else {
Err(NeedMoreParams(USERId)) }
}
"PING" => {
if !message.params.is_empty() {
Ok(PING{ token: message.params[0] })
} else {
Err(NeedMoreParams(PINGId)) }
}
"PONG" => {
if !message.params.is_empty() {
Ok(PONG{ token: message.params[0] })
} else {
Err(NeedMoreParams(PONGId)) }
}
"OPER" => {
if message.params.len() >= 2 {
Ok(OPER{ name: message.params[0],
password: message.params[1] })
} else {
Err(NeedMoreParams(OPERId)) }
}
"QUIT" => Ok(QUIT{}),
"JOIN" => {
if !message.params.is_empty() {
let mut param_it = message.params.iter();
let channels = param_it.next().unwrap().split(',').collect::<Vec<_>>();
let keys_opt = param_it.next().map(|x|
x.split(',').collect::<Vec<_>>());
if let Some(ref keys) = keys_opt {
if keys.len() != channels.len() {
return Err(ParameterDoesntMatch(JOINId, 1)); }
}
Ok(JOIN{ channels, keys: keys_opt })
} else {
Err(NeedMoreParams(JOINId)) }
}
"PART" => {
if !message.params.is_empty() {
let mut param_it = message.params.iter();
let channels = param_it.next().unwrap().split(',').collect::<Vec<_>>();
let reason = param_it.next().map(|x| *x);
Ok(PART{ channels, reason })
} else {
Err(NeedMoreParams(PARTId)) }
}
"TOPIC" => {
if !message.params.is_empty() {
let mut param_it = message.params.iter();
let channel = param_it.next().unwrap();
let topic = param_it.next().map(|x| *x);
Ok(TOPIC{ channel, topic })
} else {
Err(NeedMoreParams(TOPICId)) }
}
"NAMES" => {
if !message.params.is_empty() {
Ok(NAMES{ channels: message.params[0].split(',').collect::<Vec<_>>() })
} else {
Ok(NAMES{ channels: vec![] }) }
}
"LIST" => {
if !message.params.is_empty() {
let mut param_it = message.params.iter();
let channels = param_it.next().unwrap().split(',').collect::<Vec<_>>();
let server = param_it.next().map(|x| *x);
Ok(LIST{ channels, server })
} else {
Ok(LIST{ channels: vec![], server: None })
}
}
"INVITE" => {
if message.params.len() >= 2 {
Ok(INVITE{ nickname: message.params[0],
channel: message.params[1] })
} else {
Err(NeedMoreParams(INVITEId)) }
}
"KICK" => {
if message.params.len() >= 2 {
let mut param_it = message.params.iter();
let channel = param_it.next().unwrap();
let users = param_it.next().unwrap().split(',').collect::<Vec<_>>();
let comment = param_it.next().map(|x| *x);
Ok(KICK{ channel, users, comment })
} else {
Err(NeedMoreParams(KICKId)) }
}
"MOTD" => {
Ok(MOTD{ target: message.params.iter().next().map(|x| *x) })
}
"VERSION" => {
Ok(VERSION{ target: message.params.iter().next().map(|x| *x) })
}
"ADMIN" => {
Ok(ADMIN{ target: message.params.iter().next().map(|x| *x) })
}
"CONNECT" => {
if !message.params.is_empty() {
let mut param_it = message.params.iter();
let target_server = param_it.next().unwrap();
let port = param_it.next().map(|x| x.parse()).transpose();
let remote_server = param_it.next().map(|x| *x);
match port {
Err(_) => {
Err(WrongParameter(CONNECTId, 1))
}
Ok(p) => Ok(CONNECT{ target_server, port: p, remote_server })
}
} else {
Err(NeedMoreParams(CONNECTId)) }
}
"LUSERS" => Ok(LUSERS{}),
"TIME" => {
Ok(TIME{ server: message.params.iter().next().map(|x| *x) })
}
"STATS" => {
if !message.params.is_empty() {
let mut param_it = message.params.iter();
let query_str = param_it.next().unwrap();
let server = param_it.next().map(|x| *x);
if query_str.len() == 1 {
Ok(STATS{ query: query_str.chars().next().unwrap(), server })
} else {
Err(WrongParameter(STATSId, 0))
}
} else {
Err(NeedMoreParams(STATSId)) }
}
"LINKS" => {
if message.params.len() == 2 {
Ok(LINKS{ remote_server: Some(message.params[0]),
server_mask: Some(message.params[1]) })
} else if message.params.len() == 1 {
Ok(LINKS{ remote_server: None,
server_mask: Some(message.params[0]) })
} else {
Ok(LINKS{ remote_server: None, server_mask: None }) }
}
"HELP" => {
if !message.params.is_empty() {
Ok(HELP{ subject: Some(message.params[0]) })
} else {
Ok(HELP{ subject: None }) }
}
"INFO" => Ok(INFO{}),
"MODE" => {
if !message.params.is_empty() {
let mut modes = vec![];
let mut param_it = message.params.iter();
let target = param_it.next().unwrap();
if let Some(s) = param_it.next() {
if s.starts_with("+") || s.starts_with("-") {
let mut modestring = *s;
let mut mode_args = vec![];
while let Some(s) = param_it.next() {
if s.starts_with("+") || s.starts_with("-") {
modes.push((modestring, mode_args));
modestring = *s;
mode_args = vec![];
} else {
mode_args.push(*s);
}
}
modes.push((modestring, mode_args));
} else {
return Err(WrongParameter(MODEId, 1));
}
}
Ok(MODE{ target, modes })
} else {
Err(NeedMoreParams(MODEId)) }
}
"PRIVMSG" => {
if message.params.len() >= 2 {
Ok(PRIVMSG{ targets: message.params[0].split(',').collect::<Vec<_>>(),
text: message.params[1] })
} else {
Err(NeedMoreParams(PRIVMSGId)) }
}
"NOTICE" => {
if message.params.len() >= 2 {
Ok(NOTICE{ targets: message.params[0].split(',').collect::<Vec<_>>(),
text: message.params[1] })
} else {
Err(NeedMoreParams(NOTICEId)) }
}
"WHO" => {
if !message.params.is_empty() {
Ok(WHO{ mask: message.params[0] })
} else {
Err(NeedMoreParams(WHOId)) }
}
"WHOIS" => {
if !message.params.is_empty() {
if message.params.len() >= 2 {
Ok(WHOIS{ target: Some(message.params[0]),
nickmasks: message.params[1].split(',').collect::<Vec<_>>() })
} else {
Ok(WHOIS{ target: None, nickmasks:
message.params[0].split(',').collect::<Vec<_>>() })
}
} else {
Err(NeedMoreParams(WHOISId)) }
}
"WHOWAS" => {
if !message.params.is_empty() {
let mut param_it = message.params.iter();
let nickname = param_it.next().unwrap();
let count = param_it.next().map(|x| x.parse()).transpose();
let server = param_it.next().map(|x| *x);
match count {
Err(_) => {
Err(WrongParameter(WHOWASId, 1))
}
Ok(c) => Ok(WHOWAS{ nickname, count: c, server })
}
} else {
Err(NeedMoreParams(WHOWASId)) }
}
"KILL" => {
if message.params.len() >= 2 {
Ok(KILL{ nickname: message.params[0],
comment: message.params[1] })
} else {
Err(NeedMoreParams(KILLId)) }
}
"REHASH" => Ok(REHASH{}),
"RESTART" => Ok(RESTART{}),
"SQUIT" => {
if message.params.len() >= 2 {
Ok(SQUIT{ server: message.params[0],
comment: message.params[1] })
} else {
Err(NeedMoreParams(SQUITId)) }
}
"AWAY" => {
Ok(AWAY{ text: message.params.iter().next().map(|x| *x) })
}
"USERHOST" => {
if !message.params.is_empty() {
Ok(USERHOST{ nicknames: message.params.clone() })
} else {
Err(NeedMoreParams(USERHOSTId)) }
}
"WALLOPS" => {
if !message.params.is_empty() {
Ok(WALLOPS{ text: message.params[0] })
} else {
Err(NeedMoreParams(WALLOPSId)) }
}
s => Err(UnknownCommand(s.to_string())),
}
}
pub(crate) fn from_message(message: &Message<'a>) -> Result<Self, CommandError> {
match Self::parse_from_message(message) {
Ok(x) => {
match x.validate() {
Ok(()) => Ok(x),
Err(e) => Err(e)
}
}
Err(e) => Err(e)
}
}
fn validate(&self) -> Result<(), CommandError> {
match self {
CAP { version, .. } => {
if let Some(v) = version {
if *v < 302 { Err(WrongParameter(CAPId, 1)) }
else { Ok(()) }
} else { Ok(()) }
}
NICK{ nickname } => {
validate_username(nickname)
.map_err(|_| WrongParameter(NICKId, 0)) }
USER{ username, .. } => {
validate_username(username)
.map_err(|_| WrongParameter(USERId, 0)) }
OPER{ name, .. } => {
validate_username(name)
.map_err(|_| WrongParameter(OPERId, 0)) }
JOIN{ channels, .. } => {
channels.iter().try_for_each(|ch| validate_channel(ch))
.map_err(|_| WrongParameter(JOINId, 0)) }
PART{ channels, .. } => {
channels.iter().try_for_each(|ch| validate_channel(ch))
.map_err(|_| WrongParameter(PARTId, 0)) }
TOPIC{ channel, .. } => {
validate_channel(channel)
.map_err(|_| WrongParameter(TOPICId, 0))}
NAMES{ channels } => {
channels.iter().try_for_each(|ch| validate_channel(ch))
.map_err(|_| WrongParameter(NAMESId, 0)) }
LIST{ channels, server } => {
channels.iter().try_for_each(|ch| validate_channel(ch))
.map_err(|_| WrongParameter(LISTId, 0))?;
if let Some(srv) = server {
validate_server(srv, WrongParameter(LISTId, 1))?;
}
Ok(())
}
INVITE{ nickname, channel } => {
validate_username(nickname)
.map_err(|_| WrongParameter(INVITEId, 0))?;
validate_channel(channel)
.map_err(|_| WrongParameter(INVITEId, 1))
}
KICK{ channel, users, .. } => {
validate_channel(channel)
.map_err(|_| WrongParameter(KICKId, 0))?;
users.iter().try_for_each(|u| validate_username(u))
.map_err(|_| WrongParameter(KICKId, 1)) }
MOTD{ target } => {
if let Some(t) = target {
validate_server_mask(t, WrongParameter(MOTDId, 0))?;
}
Ok(())
}
VERSION{ target } => {
if let Some(t) = target {
validate_server_mask(t, WrongParameter(VERSIONId, 0))?;
}
Ok(())
}
ADMIN{ target } => {
if let Some(t) = target {
validate_server_mask(t,WrongParameter(ADMINId, 0))?;
}
Ok(())
}
CONNECT{ target_server, remote_server, .. } => {
validate_server(target_server, WrongParameter(CONNECTId, 0))?;
if let Some(s) = remote_server {
validate_server(s, WrongParameter(CONNECTId, 1))?;
}
Ok(())
}
TIME{ server } => {
if let Some(s) = server {
validate_server(s, WrongParameter(TIMEId, 0))?;
}
Ok(())
}
STATS{ query, server } => {
match query {
'c'|'h'|'i'|'k'|'l'|'m'|'o'|'u'|'y' => {
if let Some(s) = server {
validate_server(s, WrongParameter(STATSId, 1))?;
}
}
_ => return Err(WrongParameter(STATSId, 0)),
};
Ok(())
}
LINKS{ remote_server, server_mask } => {
if let Some(s) = remote_server {
validate_server(s, WrongParameter(LINKSId, 0))?;
if let Some(sm) = server_mask {
validate_server_mask(sm, WrongParameter(LINKSId, 1))?;
}
} else if let Some(sm) = server_mask {
validate_server_mask(sm, WrongParameter(LINKSId, 0))?;
}
Ok(())
}
MODE{ target, modes } => {
if validate_channel(target).is_ok() {
validate_channelmodes(target, modes)
} else if validate_username(target).is_ok() {
validate_usermodes(modes)
} else { Err(WrongParameter(MODEId, 0)) }
}
PRIVMSG{ targets, .. } => {
targets.iter().try_for_each(|n| validate_username(n)
.map_err(|_| WrongParameter(PRIVMSGId, 0)).or(
validate_prefixed_channel(n, WrongParameter(PRIVMSGId, 0)))) }
NOTICE{ targets, .. } => {
targets.iter().try_for_each(|n| validate_username(n)
.map_err(|_| WrongParameter(NOTICEId, 0)).or(
validate_prefixed_channel(n, WrongParameter(NOTICEId, 0)))) }
WHOIS{ target, nickmasks } => {
let next_param_idx = if let Some(t) = target {
validate_server(t, WrongParameter(WHOISId, 0))?;
1
} else { 0 };
nickmasks.iter().try_for_each(|n| validate_username(n))
.map_err(|_| WrongParameter(WHOISId, next_param_idx))
}
WHOWAS{ nickname, server, .. } => {
validate_username(nickname).map_err(|_| WrongParameter(WHOWASId, 0))?;
if let Some(s) = server {
validate_server(s, WrongParameter(WHOWASId, 2))?;
}
Ok(())
}
KILL{ nickname, .. } => {
validate_username(nickname)
.map_err(|_| WrongParameter(KILLId, 0)) }
SQUIT{ server, .. } => {
validate_server(server, WrongParameter(SQUITId, 0))?;
Ok(())
}
USERHOST{ nicknames } => {
nicknames.iter().enumerate().try_for_each(|(i,n)| validate_username(n)
.map_err(|_| WrongParameter(USERHOSTId, i)))
}
_ => Ok(())
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_message_from_shared_str() {
assert_eq!(Ok(Message{ source: None, command: "QUIT", params: vec![] }),
Message::from_shared_str("QUIT").map_err(|e| e.to_string()));
assert_eq!(Ok(Message{ source: None, command: "QUIT", params: vec![] }),
Message::from_shared_str(" QUIT").map_err(|e| e.to_string()));
assert_eq!(Ok(Message{ source: Some("source"), command: "QUIT", params: vec![] }),
Message::from_shared_str(":source QUIT").map_err(|e| e.to_string()));
assert_eq!(Ok(Message{ source: None, command: "USER",
params: vec!["guest", "0", "*", "Ronnie Reagan"] }),
Message::from_shared_str("USER guest 0 * :Ronnie Reagan")
.map_err(|e| e.to_string()));
assert_eq!(Ok(Message{ source: None, command: "USER",
params: vec!["guest", "0", "*", "Benny"] }),
Message::from_shared_str("USER guest 0 * Benny")
.map_err(|e| e.to_string()));
assert_eq!(Ok(Message{ source: None, command: "PRIVMSG",
params: vec!["bobby", ":-). Hello guy!"] }),
Message::from_shared_str("PRIVMSG bobby ::-). Hello guy!")
.map_err(|e| e.to_string()));
assert_eq!(Ok(Message{ source: Some("mati!mat@gg.com"),
command: "QUIT", params: vec![] }),
Message::from_shared_str(":mati!mat@gg.com QUIT")
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong source syntax".to_string()),
Message::from_shared_str(":mati@mat!gg.com QUIT")
.map_err(|e| e.to_string()));
}
#[test]
fn test_command_from_message() {
assert_eq!(Ok(CAP{ subcommand: CapCommand::LS, caps: None, version: None }),
Command::from_message(&Message{ source: None, command: "CAP",
params: vec![ "LS" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(CAP{ subcommand: CapCommand::LS, caps: None, version: Some(302) }),
Command::from_message(&Message{ source: None, command: "CAP",
params: vec![ "LS", "302" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(CAP{ subcommand: CapCommand::LS, caps: None, version: Some(303) }),
Command::from_message(&Message{ source: None, command: "CAP",
params: vec![ "LS", "303" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 1 in command 'CAP'".to_string()),
Command::from_message(&Message{ source: None, command: "CAP",
params: vec![ "LS", "301" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 1 in command 'CAP'".to_string()),
Command::from_message(&Message{ source: None, command: "CAP",
params: vec![ "LS", "xxx" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(CAP{ subcommand: CapCommand::LIST, caps: None, version: None }),
Command::from_message(&Message{ source: None, command: "CAP",
params: vec![ "LIST" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(CAP{ subcommand: CapCommand::REQ, version: None,
caps: Some(vec!["multi-prefix", "tls"]) }),
Command::from_message(&Message{ source: None, command: "CAP",
params: vec![ "REQ", "multi-prefix tls" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(CAP{ subcommand: CapCommand::END, caps: None, version: None }),
Command::from_message(&Message{ source: None, command: "CAP",
params: vec![ "END" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(CAP{ subcommand: CapCommand::REQ, version: None,
caps: Some(vec!["multi-prefix", "tlsx"]) }),
Command::from_message(&Message{ source: None, command: "CAP",
params: vec![ "REQ", "multi-prefix tlsx" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Unknown subcommand 'LSS' in command 'CAP'".to_string()),
Command::from_message(&Message{ source: None, command: "CAP",
params: vec![ "LSS" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'CAP' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "CAP",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(AUTHENTICATE{}),
Command::from_message(&Message{ source: None, command: "AUTHENTICATE",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(PASS{ password: "secret" }),
Command::from_message(&Message{ source: None, command: "PASS",
params: vec![ "secret" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'PASS' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "PASS",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(NICK{ nickname: "lucky" }),
Command::from_message(&Message{ source: None, command: "NICK",
params: vec![ "lucky" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'NICK'".to_string()),
Command::from_message(&Message{ source: None, command: "NICK",
params: vec![ "luc.ky" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'NICK'".to_string()),
Command::from_message(&Message{ source: None, command: "NICK",
params: vec![ "luc,ky" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'NICK'".to_string()),
Command::from_message(&Message{ source: None, command: "NICK",
params: vec![ "luc:ky" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'NICK' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "NICK",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(USER{ username: "chris", hostname: "0", servername: "*",
realname: "Chris Wood" }),
Command::from_message(&Message{ source: None, command: "USER",
params: vec![ "chris", "0", "*", "Chris Wood" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'USER'".to_string()),
Command::from_message(&Message{ source: None, command: "USER",
params: vec![ "chr:is", "0", "*", "Chris Wood" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Command 'USER' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "USER",
params: vec![ "chris", "0", "*" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(PING{ token: "xxxaaa" }),
Command::from_message(&Message{ source: None, command: "PING",
params: vec![ "xxxaaa" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'PING' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "PING",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(OPER{ name: "guru", password: "mythebestday" }),
Command::from_message(&Message{ source: None, command: "OPER",
params: vec![ "guru", "mythebestday" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'OPER'".to_string()),
Command::from_message(&Message{ source: None, command: "OPER",
params: vec![ "gu:ru", "mythebestday" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'OPER' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "OPER",
params: vec![ "guru" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(QUIT{}),
Command::from_message(&Message{ source: None, command: "QUIT",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(JOIN{ channels: vec![ "#cats", "&fruits", "#software" ],
keys: None }),
Command::from_message(&Message{ source: None, command: "JOIN",
params: vec![ "#cats,&fruits,#software" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(JOIN{ channels: vec![ "#cats", "&fruits", "#software" ],
keys: Some(vec![ "mycat", "apple", "wesnoth" ]) }),
Command::from_message(&Message{ source: None, command: "JOIN",
params: vec![ "#cats,&fruits,#software", "mycat,apple,wesnoth" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'JOIN'".to_string()),
Command::from_message(&Message{ source: None, command: "JOIN",
params: vec![ "#cats,&fru:its,#software" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'JOIN'".to_string()),
Command::from_message(&Message{ source: None, command: "JOIN",
params: vec![ "#cats,fruits,#software" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(JOIN{ channels: vec![ "#cats", "&fru.its", "#software" ],
keys: None }),
Command::from_message(&Message{ source: None, command: "JOIN",
params: vec![ "#cats,&fru.its,#software" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Parameter 1 doesn't match for command 'JOIN'".to_string()),
Command::from_message(&Message{ source: None, command: "JOIN",
params: vec![ "#cats,&fruits,#software,#countries",
"mycat,apple,wesnoth" ] }) .map_err(|e| e.to_string()));
assert_eq!(Err("Parameter 1 doesn't match for command 'JOIN'".to_string()),
Command::from_message(&Message{ source: None, command: "JOIN",
params: vec![ "#cats,&fruits,#software",
"mycat,apple,wesnoth,zizi" ] }) .map_err(|e| e.to_string()));
assert_eq!(Err("Command 'JOIN' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "JOIN",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(PART{ channels: vec![ "#dogs", "&juices", "#hardware" ],
reason: None }),
Command::from_message(&Message{ source: None, command: "PART",
params: vec![ "#dogs,&juices,#hardware" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(PART{ channels: vec![ "#dogs", "&juices", "#hardware" ],
reason: Some("I don't like these channels") }),
Command::from_message(&Message{ source: None, command: "PART",
params: vec![ "#dogs,&juices,#hardware",
"I don't like these channels" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'PART'".to_string()),
Command::from_message(&Message{ source: None, command: "PART",
params: vec![ "#dogs,&juices,#har:dware",
"I don't like these channels" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'PART' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "PART",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(TOPIC{ channel: "#gizmo", topic: None }),
Command::from_message(&Message{ source: None, command: "TOPIC",
params: vec![ "#gizmo" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(TOPIC{ channel: "#gizmo", topic: Some("Some creatures") }),
Command::from_message(&Message{ source: None, command: "TOPIC",
params: vec![ "#gizmo", "Some creatures" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'TOPIC'".to_string()),
Command::from_message(&Message{ source: None, command: "TOPIC",
params: vec![ "#giz:mo" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'TOPIC' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "TOPIC",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(NAMES{ channels: vec![ "#dogs", "&juices", "#hardware" ] }),
Command::from_message(&Message{ source: None, command: "NAMES",
params: vec![ "#dogs,&juices,#hardware" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(NAMES{ channels: vec![] }),
Command::from_message(&Message{ source: None, command: "NAMES",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'NAMES'".to_string()),
Command::from_message(&Message{ source: None, command: "NAMES",
params: vec![ "#dogs,&juices,#hard:ware" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(LIST{ channels: vec![ "#dogs", "&juices", "#hardware" ],
server: None }),
Command::from_message(&Message{ source: None, command: "LIST",
params: vec![ "#dogs,&juices,#hardware" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(LIST{ channels: vec![ "#dogs", "&juices", "#hardware" ],
server: Some("funny.checkbox.org") }),
Command::from_message(&Message{ source: None, command: "LIST",
params: vec![ "#dogs,&juices,#hardware",
"funny.checkbox.org" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(LIST{ channels: vec![], server: None }),
Command::from_message(&Message{ source: None, command: "LIST",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'LIST'".to_string()),
Command::from_message(&Message{ source: None, command: "LIST",
params: vec![ "#dogs,&juices,#har:dware",
"funny.checkbox.org" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 1 in command 'LIST'".to_string()),
Command::from_message(&Message{ source: None, command: "LIST",
params: vec![ "#dogs,&juices,#hardware",
"fnny" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(INVITE{ nickname: "greg", channel: "#plants" }),
Command::from_message(&Message{ source: None, command: "INVITE",
params: vec![ "greg", "#plants" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'INVITE'".to_string()),
Command::from_message(&Message{ source: None, command: "INVITE",
params: vec![ "gr:eg", "#plants" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 1 in command 'INVITE'".to_string()),
Command::from_message(&Message{ source: None, command: "INVITE",
params: vec![ "greg", "_plants" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'INVITE' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "INVITE",
params: vec![ "greg" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(KICK{ channel: "#toolkits", users: vec!["mickey"], comment: None }),
Command::from_message(&Message{ source: None, command: "KICK",
params: vec![ "#toolkits", "mickey" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(KICK{ channel: "#toolkits", users: vec!["mickey", "lola"],
comment: None }),
Command::from_message(&Message{ source: None, command: "KICK",
params: vec![ "#toolkits", "mickey,lola" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(KICK{ channel: "#toolkits", users: vec!["mickey"],
comment: Some("Mickey is not polite") }),
Command::from_message(&Message{ source: None, command: "KICK",
params: vec![ "#toolkits", "mickey", "Mickey is not polite" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'KICK'".to_string()),
Command::from_message(&Message{ source: None, command: "KICK",
params: vec![ "@toolkits", "mickey" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 1 in command 'KICK'".to_string()),
Command::from_message(&Message{ source: None, command: "KICK",
params: vec![ "#toolkits", "mic:key" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'KICK' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "KICK",
params: vec![ "#toolkits" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MOTD{ target: None }),
Command::from_message(&Message{ source: None, command: "MOTD",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MOTD{ target: Some("bubu.com") }),
Command::from_message(&Message{ source: None, command: "MOTD",
params: vec![ "bubu.com" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MOTD{ target: Some("*com") }),
Command::from_message(&Message{ source: None, command: "MOTD",
params: vec![ "*com" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'MOTD'".to_string()),
Command::from_message(&Message{ source: None, command: "MOTD",
params: vec![ "bubucom" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(VERSION{ target: None }),
Command::from_message(&Message{ source: None, command: "VERSION",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(VERSION{ target: Some("bubu.com") }),
Command::from_message(&Message{ source: None, command: "VERSION",
params: vec![ "bubu.com" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(VERSION{ target: Some("*com") }),
Command::from_message(&Message{ source: None, command: "VERSION",
params: vec![ "*com" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'VERSION'".to_string()),
Command::from_message(&Message{ source: None, command: "VERSION",
params: vec![ "bubucom" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(ADMIN{ target: None }),
Command::from_message(&Message{ source: None, command: "ADMIN",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(ADMIN{ target: Some("bubu.com") }),
Command::from_message(&Message{ source: None, command: "ADMIN",
params: vec![ "bubu.com" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(ADMIN{ target: Some("*com") }),
Command::from_message(&Message{ source: None, command: "ADMIN",
params: vec![ "*com" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'ADMIN'".to_string()),
Command::from_message(&Message{ source: None, command: "ADMIN",
params: vec![ "bubucom" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(CONNECT{ target_server: "chat.purple.com", port: None,
remote_server: None }),
Command::from_message(&Message{ source: None, command: "CONNECT",
params: vec![ "chat.purple.com" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(CONNECT{ target_server: "chat.purple.com", port: Some(6670),
remote_server: None }),
Command::from_message(&Message{ source: None, command: "CONNECT",
params: vec![ "chat.purple.com", "6670" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(CONNECT{ target_server: "chat.purple.com", port: Some(6670),
remote_server: Some("chat.broker.com") }),
Command::from_message(&Message{ source: None, command: "CONNECT",
params: vec![ "chat.purple.com", "6670", "chat.broker.com" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'CONNECT'".to_string()),
Command::from_message(&Message{ source: None, command: "CONNECT",
params: vec![ "chatpurplecom" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 1 in command 'CONNECT'".to_string()),
Command::from_message(&Message{ source: None, command: "CONNECT",
params: vec![ "chat.purple.com", "xxxaa" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 1 in command 'CONNECT'".to_string()),
Command::from_message(&Message{ source: None, command: "CONNECT",
params: vec![ "chat.purple.com", "6670", "chatbrokercom" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Command 'CONNECT' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "CONNECT",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(LUSERS{}),
Command::from_message(&Message{ source: None, command: "LUSERS",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(TIME{ server: None }),
Command::from_message(&Message{ source: None, command: "TIME",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(TIME{ server: Some("bubu.com") }),
Command::from_message(&Message{ source: None, command: "TIME",
params: vec![ "bubu.com" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'TIME'".to_string()),
Command::from_message(&Message{ source: None, command: "TIME",
params: vec![ "*com" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'TIME'".to_string()),
Command::from_message(&Message{ source: None, command: "TIME",
params: vec![ "bubucom" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(STATS{ query: 'c', server: None }),
Command::from_message(&Message{ source: None, command: "STATS",
params: vec![ "c" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(STATS{ query: 'l', server: None }),
Command::from_message(&Message{ source: None, command: "STATS",
params: vec![ "l" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(STATS{ query: 'o', server: None }),
Command::from_message(&Message{ source: None, command: "STATS",
params: vec![ "o" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(STATS{ query: 'o', server: Some("chat.fruits.com") }),
Command::from_message(&Message{ source: None, command: "STATS",
params: vec![ "o", "chat.fruits.com" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'STATS'".to_string()),
Command::from_message(&Message{ source: None, command: "STATS",
params: vec![ "z" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 1 in command 'STATS'".to_string()),
Command::from_message(&Message{ source: None, command: "STATS",
params: vec![ "o", "chatfruitscom" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'STATS' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "STATS",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(LINKS{ remote_server: None, server_mask: Some("*.gigo.net") }),
Command::from_message(&Message{ source: None, command: "LINKS",
params: vec![ "*.gigo.net" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(LINKS{ remote_server: Some("first.proxy.net"),
server_mask: Some("*.gigo.net") }),
Command::from_message(&Message{ source: None, command: "LINKS",
params: vec![ "first.proxy.net", "*.gigo.net" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'LINKS'".to_string()),
Command::from_message(&Message{ source: None, command: "LINKS",
params: vec![ "gigonet" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 1 in command 'LINKS'".to_string()),
Command::from_message(&Message{ source: None, command: "LINKS",
params: vec![ "first.proxy.net", "gigonet" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'LINKS'".to_string()),
Command::from_message(&Message{ source: None, command: "LINKS",
params: vec![ "firstproxynet", "*.gigo.net" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(LINKS{ remote_server: None, server_mask: None }),
Command::from_message(&Message{ source: None, command: "LINKS",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(HELP{ subject: Some("PING Message") }),
Command::from_message(&Message{ source: None, command: "HELP",
params: vec![ "PING Message" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(HELP{ subject: Some("PONG Message") }),
Command::from_message(&Message{ source: None, command: "HELP",
params: vec![ "PONG Message" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(HELP{ subject: None }),
Command::from_message(&Message{ source: None, command: "HELP",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(INFO{}),
Command::from_message(&Message{ source: None, command: "INFO",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "andy", modes: vec![] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "andy" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#engines", modes: vec![] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#engines" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "andy", modes: vec![("+ow", vec![])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "andy", "+ow" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "andy", modes: vec![("+oOr-iw",vec![])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "andy", "+oOr-iw" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "andy",
modes: vec![("+oOr",vec![]), ("-iw", vec![])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "andy", "+oOr", "-iw" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Unknown umode flag in parameter 1".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "teddy", "+otOr" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Unknown umode flag in parameter 1".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "teddy", "+otOr", "bbb" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis", modes: vec![("+ntsm", vec![])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+ntsm" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis",
modes: vec![("+b", vec![ "*@192.168.1.7" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+b", "*@192.168.1.7" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis",
modes: vec![("-b", vec![ "*@192.168.1.7" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-b", "*@192.168.1.7" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis",
modes: vec![("+b", vec![ "*@192.168.1.7" ]), ("+e", vec![ "*@192.168.1.3" ]),
("+I", vec![ "*@192.168.1.9" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+b", "*@192.168.1.7", "+e", "*@192.168.1.3",
"+I", "*@192.168.1.9" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis",
modes: vec![("-b", vec![ "*@192.168.1.7" ]), ("-e", vec![ "*@192.168.1.3" ]),
("-I", vec![ "*@192.168.1.9" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-b", "*@192.168.1.7", "-e", "*@192.168.1.3",
"-I", "*@192.168.1.9" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis",
modes: vec![("-mb", vec![ "*@192.168.1.7" ]), ("-ne", vec![ "*@192.168.1.3" ]),
("-tI", vec![ "*@192.168.1.9" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-mb", "*@192.168.1.7", "-ne", "*@192.168.1.3",
"-tI", "*@192.168.1.9" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis", modes: vec![("+b", vec![])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+b" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis", modes: vec![("+l", vec![ "123" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+l", "123" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis", modes: vec![("-l", vec![])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-l" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis", modes: vec![("+k", vec![ "secret" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+k", "secret" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis", modes: vec![("-k", vec![])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-k" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis",
modes: vec![("-bl+i", vec![ "*@192.168.0.1" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-bl+i", "*@192.168.0.1" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis", modes: vec![("+o", vec![ "barry" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+o", "barry" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis", modes: vec![("-o", vec![ "barry" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-o", "barry" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis", modes: vec![("+h", vec![ "barry" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+h", "barry" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis", modes: vec![("-h", vec![ "barry" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-h", "barry" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis", modes: vec![("+v", vec![ "barry" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+v", "barry" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "#chasis", modes: vec![("-v", vec![ "barry" ])] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-v", "barry" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(MODE{ target: "andy", modes: vec![] }),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "andy" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Invalid mode parameter: #chasis l No argument".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+l" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Invalid mode parameter: #chasis k No argument".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+k" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Invalid mode parameter: #chasis o No argument".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+o" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Invalid mode parameter: #chasis o No argument".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-o" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Invalid mode parameter: #chasis h No argument".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+h" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Invalid mode parameter: #chasis h No argument".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-h" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Invalid mode parameter: #chasis v No argument".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+v" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Invalid mode parameter: #chasis v No argument".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-v" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Invalid mode parameter: #chasis l xxx invalid digit found in \
string".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "+l", "xxx" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Invalid mode parameter: #chasis l 145 \
Unexpected argument".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-l", "145" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Invalid mode parameter: #chasis k 145 \
Unexpected argument".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-k", "145" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Invalid mode parameter: #chasis o No argument".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![ "#chasis", "-bl+oi", "*@192.168.0.1" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Command 'MODE' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "MODE",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(PRIVMSG{ targets: vec![ "bobby", "andy" ], text: "Hello, guys" }),
Command::from_message(&Message{ source: None, command: "PRIVMSG",
params: vec![ "bobby,andy" , "Hello, guys" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(PRIVMSG{ targets: vec![ "#graphics", "&musics" ],
text: "Hello, guys" }),
Command::from_message(&Message{ source: None, command: "PRIVMSG",
params: vec![ "#graphics,&musics" , "Hello, guys" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(PRIVMSG{ targets: vec![ "#graphics", "&musics", "jimmy" ],
text: "Hello, cruel world!" }),
Command::from_message(&Message{ source: None, command: "PRIVMSG",
params: vec![ "#graphics,&musics,jimmy" , "Hello, cruel world!" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(PRIVMSG{ targets: vec![ "%#graphics", "~&musics", "jimmy" ],
text: "Hello, cruel world!" }),
Command::from_message(&Message{ source: None, command: "PRIVMSG",
params: vec![ "%#graphics,~&musics,jimmy" , "Hello, cruel world!" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(PRIVMSG{ targets: vec![ "@#graphics", "&&musics", "jimmy" ],
text: "Hello, cruel world!" }),
Command::from_message(&Message{ source: None, command: "PRIVMSG",
params: vec![ "@#graphics,&&musics,jimmy" , "Hello, cruel world!" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(PRIVMSG{ targets: vec![ "@+#graphics", "&&musics", "jimmy" ],
text: "Hello, cruel world!" }),
Command::from_message(&Message{ source: None, command: "PRIVMSG",
params: vec![ "@+#graphics,&&musics,jimmy" , "Hello, cruel world!" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(PRIVMSG{ targets: vec![ "+#graphics", "+&musics", "jimmy" ],
text: "Hello, cruel world!" }),
Command::from_message(&Message{ source: None, command: "PRIVMSG",
params: vec![ "+#graphics,+&musics,jimmy" , "Hello, cruel world!" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'PRIVMSG'".to_string()),
Command::from_message(&Message{ source: None, command: "PRIVMSG",
params: vec![ "#graphics,&musics,ji.mmy", "Hello, cruel world!" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Command 'PRIVMSG' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "PRIVMSG",
params: vec![ "#graphics,&musics,jimmy" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(NOTICE{ targets: vec![ "bobby", "andy" ], text: "Hello, guys" }),
Command::from_message(&Message{ source: None, command: "NOTICE",
params: vec![ "bobby,andy" , "Hello, guys" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(NOTICE{ targets: vec![ "#graphics", "&musics" ],
text: "Hello, guys" }),
Command::from_message(&Message{ source: None, command: "NOTICE",
params: vec![ "#graphics,&musics" , "Hello, guys" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(NOTICE{ targets: vec![ "#graphics", "&musics", "jimmy" ],
text: "Hello, cruel world!" }),
Command::from_message(&Message{ source: None, command: "NOTICE",
params: vec![ "#graphics,&musics,jimmy" , "Hello, cruel world!" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'NOTICE'".to_string()),
Command::from_message(&Message{ source: None, command: "NOTICE",
params: vec![ "#graphics,&musics,ji.mmy", "Hello, cruel world!" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Command 'NOTICE' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "NOTICE",
params: vec![ "#graphics,&musics,jimmy" ] })
.map_err(|e| e.to_string()));
assert_eq!(Ok(WHO{ mask: "bla*bla" }),
Command::from_message(&Message{ source: None, command: "WHO",
params: vec![ "bla*bla" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'WHO' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "WHO",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(WHOIS{ target: None, nickmasks: vec![ "alice", "eliz", "garry" ] }),
Command::from_message(&Message{ source: None, command: "WHOIS",
params: vec![ "alice,eliz,garry" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(WHOIS{ target: Some("coco.net"),
nickmasks: vec![ "alice", "eliz", "garry" ] }),
Command::from_message(&Message{ source: None, command: "WHOIS",
params: vec![ "coco.net", "alice,eliz,garry" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'WHOIS'".to_string()),
Command::from_message(&Message{ source: None, command: "WHOIS",
params: vec![ "alice,el:iz,garry" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'WHOIS'".to_string()),
Command::from_message(&Message{ source: None, command: "WHOIS",
params: vec![ "coconet", "alice,eliz,garry" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 1 in command 'WHOIS'".to_string()),
Command::from_message(&Message{ source: None, command: "WHOIS",
params: vec![ "coco.net", "alice,eliz,ga:rry" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Command 'WHOIS' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "WHOIS",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(WHOWAS{ nickname: "mat", count: None, server: None }),
Command::from_message(&Message{ source: None, command: "WHOWAS",
params: vec![ "mat" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(WHOWAS{ nickname: "mat", count: Some(10), server: None }),
Command::from_message(&Message{ source: None, command: "WHOWAS",
params: vec![ "mat", "10" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(WHOWAS{ nickname: "mat", count: Some(10),
server: Some("some.where.net") }),
Command::from_message(&Message{ source: None, command: "WHOWAS",
params: vec![ "mat", "10", "some.where.net" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'WHOWAS'".to_string()),
Command::from_message(&Message{ source: None, command: "WHOWAS",
params: vec![ "mat:" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 1 in command 'WHOWAS'".to_string()),
Command::from_message(&Message{ source: None, command: "WHOWAS",
params: vec![ "mat", "XX" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 2 in command 'WHOWAS'".to_string()),
Command::from_message(&Message{ source: None, command: "WHOWAS",
params: vec![ "mat", "10", "somewherenet" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Command 'WHOWAS' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "WHOWAS",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(KILL{ nickname: "bobby", comment: "Killed!" }),
Command::from_message(&Message{ source: None, command: "KILL",
params: vec![ "bobby", "Killed!" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'KILL'".to_string()),
Command::from_message(&Message{ source: None, command: "KILL",
params: vec![ "bob:by", "Killed!" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'KILL' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "KILL",
params: vec![ "bobby" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(REHASH{}),
Command::from_message(&Message{ source: None, command: "REHASH",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(RESTART{}),
Command::from_message(&Message{ source: None, command: "RESTART",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(SQUIT{ server: "somewhere.dot.com", comment: "Killed!" }),
Command::from_message(&Message{ source: None, command: "SQUIT",
params: vec![ "somewhere.dot.com", "Killed!" ] })
.map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'SQUIT'".to_string()),
Command::from_message(&Message{ source: None, command: "SQUIT",
params: vec![ "bobxxx", "Killed!" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'SQUIT' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "SQUIT",
params: vec![ "somewhere.dot.com" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(AWAY{ text: None }),
Command::from_message(&Message{ source: None, command: "AWAY",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(AWAY{ text: Some("I will make rest") }),
Command::from_message(&Message{ source: None, command: "AWAY",
params: vec![ "I will make rest" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(USERHOST{ nicknames: vec![ "bobby" ] }),
Command::from_message(&Message{ source: None, command: "USERHOST",
params: vec![ "bobby" ] }).map_err(|e| e.to_string()));
assert_eq!(Ok(USERHOST{ nicknames: vec![ "bobby", "jimmy" ] }),
Command::from_message(&Message{ source: None, command: "USERHOST",
params: vec![ "bobby", "jimmy" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 0 in command 'USERHOST'".to_string()),
Command::from_message(&Message{ source: None, command: "USERHOST",
params: vec![ "bo:bby", "jimmy" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 1 in command 'USERHOST'".to_string()),
Command::from_message(&Message{ source: None, command: "USERHOST",
params: vec![ "bobby", "ji:mmy" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Wrong parameter 2 in command 'USERHOST'".to_string()),
Command::from_message(&Message{ source: None, command: "USERHOST",
params: vec![ "bobby", "damon", "ji:mmy" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'USERHOST' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "USERHOST",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(WALLOPS{ text: "This is some message" }),
Command::from_message(&Message{ source: None, command: "WALLOPS",
params: vec![ "This is some message" ] }).map_err(|e| e.to_string()));
assert_eq!(Err("Command 'WALLOPS' needs more parameters".to_string()),
Command::from_message(&Message{ source: None, command: "WALLOPS",
params: vec![] }).map_err(|e| e.to_string()));
assert_eq!(Ok(RESTART{}),
Command::from_message(&Message{ source: None, command: "reStaRt",
params: vec![] }).map_err(|e| e.to_string()));
}
#[test]
fn test_message_to_string_with_source() {
assert_eq!(":buru USER guest 0 * :Ronnie Reagan".to_string(),
Message{ source: None, command: "USER",
params: vec!["guest", "0", "*", "Ronnie Reagan"] }
.to_string_with_source("buru"));
assert_eq!(":buru USER guest 0 * :".to_string(),
Message{ source: None, command: "USER",
params: vec!["guest", "0", "*", ""] }
.to_string_with_source("buru"));
assert_eq!(":sonny INVITE mati1 #xxx".to_string(),
Message{ source: Some("xxxx"), command: "INVITE",
params: vec!["mati1", "#xxx"] }.to_string_with_source("sonny"));
}
}