use crate::command::IRCCommand;
use crate::error::{MessageError, MessageErrorDetails};
#[derive(Debug)]
pub struct Message {
pub source: Option<String>,
pub command: IRCCommand,
pub params: Vec<String>,
}
impl Message {
pub fn serialize(self) -> Vec<u8> {
let mut ret_val: Vec<u8> = vec![];
if self.source.is_some() {
ret_val = [
":".as_bytes().to_vec(),
self.source.unwrap().as_bytes().to_vec(),
" ".as_bytes().to_vec(),
]
.concat();
}
ret_val = [
ret_val,
self.command.command_text().as_bytes().to_vec(),
" ".as_bytes().to_vec(),
self.params.join(" ").into(),
]
.concat();
ret_val
}
pub fn parse(raw_input: Vec<u8>, source_client: Option<String>) -> Result<Self, MessageError> {
if raw_input.len() < 1 {
return Err(MessageError {
detail: MessageErrorDetails::NoData,
});
}
let split_message: Vec<Vec<u8>> = raw_input.into_iter().fold(Vec::new(), |mut acc, x| {
if x == 32 || acc.is_empty() {
acc.push(Vec::new());
}
if x != 32 {
acc.last_mut().unwrap().push(x);
}
acc
});
let mut source = source_client.clone();
let mut command_index = 0;
if split_message[0][0] == 58 {
command_index = 1;
let parsed_source = match Message::parse_prefix(split_message[0].clone()) {
Ok(x) => x,
Err(y) => return Err(y),
};
source = source_client.or_else(|| Some(parsed_source));
}
if split_message.len() < command_index + 1 {
return Err(MessageError {
detail: MessageErrorDetails::NoCommand,
});
}
let irc_command = match Message::parse_command(split_message[command_index].clone()) {
Ok(x) => x,
Err(y) => return Err(y),
};
let params = match irc_command.parse_params(split_message[command_index + 1..].to_vec()) {
Ok(x) => x,
Err(y) => return Err(y),
};
Ok(Message {
source: source,
command: irc_command,
params: params
.iter()
.map(|p| String::from_utf8_lossy(p).into_owned())
.collect(),
})
}
fn parse_command(command_input: Vec<u8>) -> Result<IRCCommand, MessageError> {
let raw_command = match String::from_utf8(command_input) {
Ok(x) => x,
Err(_) => {
return Err(MessageError {
detail: MessageErrorDetails::FailedCommandParse,
})
}
};
match raw_command.as_str() {
"NAMES" => return Ok(IRCCommand::NAMES),
"NICK" => return Ok(IRCCommand::NICK),
"PRIVMSG" => return Ok(IRCCommand::PRIVMSG),
"001" => return Ok(IRCCommand::RPL_WELCOME),
"353" => return Ok(IRCCommand::RPL_NAMREPLY),
"366" => return Ok(IRCCommand::RPL_ENDOFNAMES),
_ => {
return Err(MessageError {
detail: MessageErrorDetails::InvalidCommand,
})
}
}
}
fn parse_prefix(prefix_input: Vec<u8>) -> Result<String, MessageError> {
if prefix_input.len() < 2 {
return Err(MessageError {
detail: MessageErrorDetails::NoClient,
});
}
if prefix_input[0] == 58 {
match String::from_utf8(prefix_input.clone().split_off(1)) {
Ok(x) => return Ok(x),
Err(_) => {
return Err(MessageError {
detail: MessageErrorDetails::FailedPrefixParse,
});
}
}
} else {
return Err(MessageError {
detail: MessageErrorDetails::NoPrefix,
});
}
}
}
#[test]
fn test_valid_command() {
let message = "NAMES".as_bytes().to_vec();
let command = Message::parse_command(message);
assert_eq!(command, Ok(IRCCommand::NAMES));
}
#[test]
fn test_invalid_command() {
let message = "NAMES2".as_bytes().to_vec();
let command = Message::parse_command(message);
assert!(command.is_err());
}
#[test]
fn test_blank_command() {
let message = "".as_bytes().to_vec();
let command = Message::parse_command(message);
assert!(command.is_err());
}
#[test]
fn test_valid_source() {
let message = ":TestClient".as_bytes().to_vec();
let source = Message::parse_prefix(message);
let source_string = match source {
Ok(x) => x,
Err(_) => "None".to_string(),
};
assert_eq!(source_string, "TestClient");
}
#[test]
fn test_invalid_source() {
let message = ":".as_bytes().to_vec();
let source = Message::parse_prefix(message);
let source_string = match source {
Ok(x) => x,
Err(y) => y.detail.error_text().to_string(),
};
assert_eq!(source_string, "No client identifier");
}
#[test]
fn test_empty_source() {
let message = Vec::new();
let source = Message::parse_prefix(message);
let source_string = match source {
Ok(x) => x,
Err(y) => y.detail.error_text().to_string(),
};
assert_eq!(source_string, "No client identifier");
}
#[test]
fn test_format_source() {
let message = "TestClient".as_bytes().to_vec();
let source = Message::parse_prefix(message);
let source_string = match source {
Ok(x) => x,
Err(y) => y.detail.error_text().to_string(),
};
assert_eq!(source_string, "Missing : character");
}
#[test]
fn test_serialize() {
let message = Message {
source: Some("Anon".to_string()),
command: IRCCommand::NICK,
params: vec!["nonA".to_string()],
};
let output = message.serialize();
println!("{:?}", output);
assert_eq!(output.len(), 15);
assert_eq!(output[output.len() - 1], 65);
let message = Message {
source: None,
command: IRCCommand::NICK,
params: vec!["nonA".to_string()],
};
let output = message.serialize();
println!("{:?}", output);
assert_eq!(output.len(), 9);
assert_eq!(output[output.len() - 1], 65);
}