use super::*; /// Client-to-server command. #[derive(Clone, Debug, PartialEq, Eq)] pub enum ClientMessage { /// CAP. Capability-related commands. Capability { subcommand: CapabilitySubcommand, }, /// PING Ping { token: Box, }, /// PONG Pong { token: Box, }, /// NICK Nick { nickname: Box, }, /// USER 0 * : User { username: Box, realname: Box, }, /// JOIN Join(Chan), /// MODE Mode { target: Recipient, }, /// WHO Who { target: Recipient, // aka mask }, /// TOPIC : Topic { chan: Chan, topic: Box, }, Part { chan: Chan, message: Box, }, /// PRIVMSG : PrivateMessage { recipient: Recipient, body: Box, }, /// QUIT : Quit { reason: Box, }, } pub fn client_message(input: &str) -> IResult<&str, 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: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("CAP ")(input)?; let (input, subcommand) = capability_subcommand(input)?; Ok((input, ClientMessage::Capability { subcommand })) } fn client_message_ping(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("PING ")(input)?; let (input, token) = token(input)?; Ok(( input, ClientMessage::Ping { token: token.into(), }, )) } fn client_message_pong(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("PONG ")(input)?; let (input, token) = token(input)?; Ok(( input, ClientMessage::Pong { token: token.into(), }, )) } fn client_message_nick(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("NICK ")(input)?; let (input, nickname) = receiver(input)?; Ok(( input, ClientMessage::Nick { nickname: nickname.into(), }, )) } fn client_message_user(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("USER ")(input)?; let (input, username) = receiver(input)?; let (input, _) = tag(" ")(input)?; let (input, _) = receiver(input)?; let (input, _) = tag(" ")(input)?; let (input, _) = receiver(input)?; let (input, _) = tag(" :")(input)?; let (input, realname) = token(input)?; Ok(( input, ClientMessage::User { username: username.into(), realname: realname.into(), }, )) } fn client_message_join(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("JOIN ")(input)?; let (input, chan) = chan(input)?; Ok((input, ClientMessage::Join(chan))) } fn client_message_mode(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("MODE ")(input)?; let (input, target) = recipient(input)?; Ok((input, ClientMessage::Mode { target })) } fn client_message_who(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("WHO ")(input)?; let (input, target) = recipient(input)?; Ok((input, ClientMessage::Who { target })) } fn client_message_topic(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("TOPIC ")(input)?; let (input, chan) = chan(input)?; let (input, _) = tag(" :")(input)?; let (input, topic) = token(input)?; let topic = topic.into(); Ok((input, ClientMessage::Topic { chan, topic })) } fn client_message_part(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("PART ")(input)?; let (input, chan) = chan(input)?; let (input, _) = tag(" :")(input)?; let (input, message) = token(input)?; let message = message.into(); Ok((input, ClientMessage::Part { chan, message })) } fn client_message_privmsg(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("PRIVMSG ")(input)?; let (input, recipient) = recipient(input)?; let (input, _) = tag(" :")(input)?; let (input, body) = token(input)?; let body = body.into(); Ok((input, ClientMessage::PrivateMessage { recipient, body })) } fn client_message_quit(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag("QUIT :")(input)?; let (input, reason) = token(input)?; Ok(( input, ClientMessage::Quit { reason: reason.into(), }, )) } #[derive(Clone, Debug, PartialEq, Eq)] pub enum CapabilitySubcommand { /// CAP LS {code} List { code: [u8; 3] }, /// CAP END End, } fn capability_subcommand(input: &str) -> IResult<&str, CapabilitySubcommand> { alt((capability_subcommand_ls, capability_subcommand_end))(input) } fn capability_subcommand_ls(input: &str) -> IResult<&str, CapabilitySubcommand> { let (input, _) = tag("LS ")(input)?; let (input, code) = take(3usize)(input)?; Ok(( input, CapabilitySubcommand::List { code: code.as_bytes().try_into().unwrap(), }, )) } fn capability_subcommand_end(input: &str) -> IResult<&str, 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 = "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 = "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 = "PING 1337"; let expected = ClientMessage::Ping { token: "1337".into(), }; let result = client_message(input); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); } #[test] fn test_client_message_pong() { let input = "PONG 1337"; let expected = ClientMessage::Pong { token: "1337".into(), }; let result = client_message(input); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); } #[test] fn test_client_message_nick() { let input = "NICK SomeNick"; let expected = ClientMessage::Nick { nickname: "SomeNick".into(), }; let result = client_message(input); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); } #[test] fn test_client_message_user() { let input = "USER SomeNick 8 * :Real Name"; let expected = ClientMessage::User { username: "SomeNick".into(), realname: "Real Name".into(), }; let result = client_message(input); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); } #[test] fn test_client_message_part() { let input = "PART #chan :Pokasiki !!!"; let expected = ClientMessage::Part { chan: Chan::Global("chan".into()), message: "Pokasiki !!!".into(), }; let result = client_message(input); assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result)); } }