use super::*; /// Client-to-server command. #[derive(Clone, Debug, PartialEq, Eq)] pub enum ClientMessage { /// CAP. Capability-related commands. Capability { subcommand: CapabilitySubcommand, }, /// PING Ping { token: ByteVec, }, /// PONG Pong { token: ByteVec, }, /// NICK Nick { nickname: ByteVec, }, /// USER 0 * : User { username: ByteVec, realname: ByteVec, }, /// JOIN Join(Chan), /// MODE Mode(Chan), // TODO support not only chan /// WHO Who(Chan), // TODO support not only chan /// TOPIC : Topic { chan: Chan, topic: ByteVec, }, Part { chan: Chan, message: ByteVec, }, /// PRIVMSG : PrivateMessage { recipient: Recipient, body: ByteVec, }, /// QUIT : Quit { reason: ByteVec, }, } pub fn client_message(input: &[u8]) -> IResult<&[u8], ClientMessage> { alt(( client_message_capability, client_message_ping, client_message_pong, client_message_nick, client_message_user, client_message_join, client_message_mode, client_message_who, client_message_topic, client_message_part, client_message_privmsg, client_message_quit, ))(input) } fn client_message_capability(input: &[u8]) -> IResult<&[u8], ClientMessage> { let (input, _) = tag("CAP ")(input)?; let (input, subcommand) = capability_subcommand(input)?; Ok((input, ClientMessage::Capability { subcommand })) } fn client_message_ping(input: &[u8]) -> IResult<&[u8], ClientMessage> { let (input, _) = tag("PING ")(input)?; let (input, token) = token(input)?; Ok(( input, ClientMessage::Ping { token: token.to_owned(), }, )) } fn client_message_pong(input: &[u8]) -> IResult<&[u8], ClientMessage> { let (input, _) = tag("PONG ")(input)?; let (input, token) = token(input)?; Ok(( input, ClientMessage::Pong { token: token.to_owned(), }, )) } fn client_message_nick(input: &[u8]) -> IResult<&[u8], ClientMessage> { let (input, _) = tag("NICK ")(input)?; let (input, nickname) = receiver(input)?; Ok(( input, ClientMessage::Nick { nickname: nickname.to_owned(), }, )) } fn client_message_user(input: &[u8]) -> IResult<&[u8], ClientMessage> { let (input, _) = tag("USER ")(input)?; let (input, username) = receiver(input)?; let (input, _) = tag(" ")(input)?; let (input, _) = take(1_usize)(input)?; // 0 in spec, but any in fact let (input, _) = tag(" * :")(input)?; let (input, realname) = token(input)?; Ok(( input, ClientMessage::User { username: username.to_owned(), realname: realname.to_owned(), }, )) } fn client_message_join(input: &[u8]) -> IResult<&[u8], ClientMessage> { let (input, _) = tag("JOIN ")(input)?; let (input, chan) = chan(input)?; Ok((input, ClientMessage::Join(chan))) } fn client_message_mode(input: &[u8]) -> IResult<&[u8], ClientMessage> { let (input, _) = tag("MODE ")(input)?; let (input, chan) = chan(input)?; Ok((input, ClientMessage::Mode(chan))) } fn client_message_who(input: &[u8]) -> IResult<&[u8], ClientMessage> { let (input, _) = tag("WHO ")(input)?; let (input, chan) = chan(input)?; Ok((input, ClientMessage::Who(chan))) } fn client_message_topic(input: &[u8]) -> IResult<&[u8], ClientMessage> { let (input, _) = tag("TOPIC ")(input)?; let (input, chan) = chan(input)?; let (input, _) = tag(b" :")(input)?; let (input, topic) = token(input)?; let topic = topic.to_vec(); Ok((input, ClientMessage::Topic { chan, topic })) } fn client_message_part(input: &[u8]) -> IResult<&[u8], ClientMessage> { let (input, _) = tag("PART ")(input)?; let (input, chan) = chan(input)?; let (input, _) = tag(b" :")(input)?; let (input, message) = token(input)?; let message = message.to_vec(); Ok((input, ClientMessage::Part { chan, message })) } fn client_message_privmsg(input: &[u8]) -> IResult<&[u8], ClientMessage> { let (input, _) = tag("PRIVMSG ")(input)?; let (input, recipient) = recipient(input)?; let (input, _) = tag(" :")(input)?; let (input, body) = token(input)?; let body = body.to_vec(); Ok((input, ClientMessage::PrivateMessage { recipient, body })) } fn client_message_quit(input: &[u8]) -> IResult<&[u8], ClientMessage> { let (input, _) = tag("QUIT :")(input)?; let (input, reason) = token(input)?; Ok(( input, ClientMessage::Quit { reason: reason.to_vec(), }, )) } #[derive(Clone, Debug, PartialEq, Eq)] pub enum CapabilitySubcommand { /// CAP LS {code} List { code: [u8; 3] }, /// CAP END End, } fn capability_subcommand(input: &[u8]) -> IResult<&[u8], CapabilitySubcommand> { alt((capability_subcommand_ls, capability_subcommand_end))(input) } fn capability_subcommand_ls(input: &[u8]) -> IResult<&[u8], CapabilitySubcommand> { let (input, _) = tag("LS ")(input)?; let (input, code) = take(3usize)(input)?; Ok(( input, CapabilitySubcommand::List { code: code.try_into().unwrap(), }, )) } fn capability_subcommand_end(input: &[u8]) -> IResult<&[u8], CapabilitySubcommand> { let (input, _) = tag("END")(input)?; Ok((input, CapabilitySubcommand::End)) } #[cfg(test)] mod test { use assert_matches::*; use super::*; #[test] fn test_client_message_cap_ls() { let input = b"CAP LS 302"; let expected = ClientMessage::Capability { subcommand: CapabilitySubcommand::List { code: *b"302" }, }; let result = client_message(input); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); } #[test] fn test_client_message_cap_end() { let input = b"CAP END"; let expected = ClientMessage::Capability { subcommand: CapabilitySubcommand::End, }; let result = client_message(input); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); } #[test] fn test_client_message_ping() { let input = b"PING 1337"; let expected = ClientMessage::Ping { token: b"1337".to_vec(), }; let result = client_message(input); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); } #[test] fn test_client_message_pong() { let input = b"PONG 1337"; let expected = ClientMessage::Pong { token: b"1337".to_vec(), }; let result = client_message(input); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); } #[test] fn test_client_message_nick() { let input = b"NICK SomeNick"; let expected = ClientMessage::Nick { nickname: b"SomeNick".to_vec(), }; let result = client_message(input); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); } #[test] fn test_client_message_user() { let input = b"USER SomeNick 8 * :Real Name"; let expected = ClientMessage::User { username: b"SomeNick".to_vec(), realname: b"Real Name".to_vec(), }; let result = client_message(input); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); } #[test] fn test_client_message_part() { let input = b"PART #chan :Pokasiki !!!"; let expected = ClientMessage::Part { chan: Chan::Global(b"chan".to_vec()), message: b"Pokasiki !!!".to_vec(), }; let result = client_message(input); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); } }