use code::Code;
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum ParseError {
EmptyCommand,
EmptyMessage,
UnexpectedEnd,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Message {
pub prefix: Option<Prefix>,
pub code: Code,
pub args: Vec<String>,
}
impl Message {
pub fn parse(line: &str) -> Result<Message, ParseError> {
if line.len() == 0 || line.trim().len() == 0 {
return Err(ParseError::EmptyMessage);
}
let mut state = line.trim_right_matches("\r\n");
let mut prefix: Option<Prefix> = None;
let code: Option<&str>;
let mut args: Vec<String> = Vec::new();
if state.starts_with(":") {
match state.find(" ") {
None => return Err(ParseError::UnexpectedEnd),
Some(idx) => {
prefix = parse_prefix(&state[1..idx]);
state = &state[idx + 1..];
}
}
}
match state.find(" ") {
None => {
if state.len() == 0 {
return Err(ParseError::EmptyMessage);
} else {
code = Some(&state[..]);
state = &state[state.len()..];
}
}
Some(idx) => {
code = Some(state[..idx].into());
state = &state[idx + 1..];
}
}
if state.len() > 0 {
loop {
if state.starts_with(":") {
args.push(state[1..].into());
break;
} else {
match state.find(" ") {
None => {
args.push(state[..].into());
break;
}
Some(idx) => {
args.push(state[..idx].into());
state = &state[idx + 1..];
}
}
}
}
}
let code = match code {
None => return Err(ParseError::EmptyCommand),
Some(text) => {
match text.parse() {
Ok(code) => code,
Err(_) => Code::Unknown(text.into()),
}
}
};
Ok(Message {
prefix: prefix,
code: code,
args: args,
})
}
}
fn parse_prefix(prefix: &str) -> Option<Prefix> {
match prefix.find("!") {
None => Some(Prefix::Server(prefix.to_string())),
Some(excpos) => {
let nick = &prefix[..excpos];
let rest = &prefix[excpos + 1..];
match rest.find("@") {
None => return None,
Some(atpos) => {
let user = &rest[..atpos];
let host = &rest[atpos + 1..];
return Some(Prefix::User(PrefixUser::new(nick, user, host)));
}
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Prefix {
User(PrefixUser),
Server(String),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PrefixUser {
pub nickname: String,
pub username: String,
pub hostname: String,
}
impl PrefixUser {
fn new(nick: &str, user: &str, host: &str) -> PrefixUser {
PrefixUser {
nickname: nick.into(),
username: user.into(),
hostname: host.into(),
}
}
}
#[test]
fn test_full() {
let res = Message::parse(":org.prefix.cool COMMAND arg1 arg2 arg3 :suffix is pretty cool yo");
assert!(res.is_ok());
let msg = res.ok().unwrap();
assert_eq!(msg.code, Code::Unknown("COMMAND".to_string()));
assert_eq!(msg.args, vec!["arg1", "arg2", "arg3", "suffix is pretty cool yo"]);
}
#[test]
fn test_no_prefix() {
let res = Message::parse("NICK arg1 arg2 arg3 :suffix is pretty cool yo");
assert!(res.is_ok());
let msg = res.ok().unwrap();
assert_eq!(msg.prefix, None);
assert_eq!(msg.code, Code::Nick);
assert_eq!(msg.args, vec!["arg1", "arg2", "arg3", "suffix is pretty cool yo"]);
}
#[test]
fn test_no_suffix() {
let res = Message::parse(":org.prefix.cool NICK arg1 arg2 arg3");
assert!(res.is_ok());
let msg = res.ok().unwrap();
assert_eq!(msg.code, Code::Nick);
assert_eq!(msg.args, vec!["arg1", "arg2", "arg3"]);
}
#[test]
fn test_no_args() {
let res = Message::parse(":org.prefix.cool NICK :suffix is pretty cool yo");
assert!(res.is_ok());
let msg = res.ok().unwrap();
assert_eq!(msg.code, Code::Nick);
assert_eq!(msg.args, vec!["suffix is pretty cool yo"]);
}
#[test]
fn test_only_command() {
let res = Message::parse("NICK");
assert!(res.is_ok());
let msg = res.ok().unwrap();
assert_eq!(msg.prefix, None);
assert_eq!(msg.code, Code::Nick);
assert_eq!(msg.args.len(), 0);
}
#[test]
fn test_empty_message() {
let res = Message::parse("");
assert!(res.is_err());
let err = res.err().unwrap();
assert!(err == ParseError::EmptyMessage);
}
#[test]
fn test_empty_message_trim() {
let res = Message::parse(" ");
assert!(res.is_err());
let err = res.err().unwrap();
assert!(err == ParseError::EmptyMessage);
}
#[test]
fn test_only_prefix() {
let res = Message::parse(":org.prefix.cool");
assert!(res.is_err());
let err = res.err().unwrap();
assert!(err == ParseError::UnexpectedEnd);
}
#[test]
fn test_prefix_none() {
let res = Message::parse("COMMAND :suffix is pretty cool yo");
assert!(res.is_ok());
let msg = res.ok().unwrap();
assert_eq!(msg.args, vec!["suffix is pretty cool yo"]);
}
#[test]
fn test_prefix_server() {
let res = Message::parse(":irc.freenode.net COMMAND :suffix is pretty cool yo");
assert!(res.is_ok());
let msg = res.ok().unwrap();
assert_eq!(msg.prefix, Some(Prefix::Server("irc.freenode.net".into())));
}
#[test]
fn test_prefix_user() {
let res = Message::parse(":bob!bob@bob.com COMMAND :suffix is pretty cool yo");
assert!(res.is_ok());
let msg = res.ok().unwrap();
assert_eq!(msg.prefix, Some(Prefix::User(PrefixUser::new("bob", "bob", "bob.com"))));
}