diff --git a/src/protos/irc/client.rs b/src/protos/irc/client.rs index 8a936a4..fc653ab 100644 --- a/src/protos/irc/client.rs +++ b/src/protos/irc/client.rs @@ -7,13 +7,25 @@ pub enum ClientMessage { Capability { subcommand: CapabilitySubcommand, }, - /// PING + /// PING Ping { token: ByteVec, }, + /// PONG Pong { token: ByteVec, }, + /// NICK + Nick { + nickname: ByteVec, + }, + User { + username: ByteVec, + realname: ByteVec, + }, + Quit { + reason: ByteVec, + }, } pub fn client_message(input: &[u8]) -> IResult<&[u8], ClientMessage> { @@ -21,6 +33,9 @@ pub fn client_message(input: &[u8]) -> IResult<&[u8], ClientMessage> { client_message_capability, client_message_ping, client_message_pong, + client_message_nick, + client_message_user, + client_message_quit, ))(input) } @@ -55,6 +70,47 @@ fn client_message_pong(input: &[u8]) -> IResult<&[u8], ClientMessage> { )) } +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_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} @@ -128,6 +184,27 @@ mod test { 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)); } diff --git a/src/protos/irc/mod.rs b/src/protos/irc/mod.rs index af328e8..872a36a 100644 --- a/src/protos/irc/mod.rs +++ b/src/protos/irc/mod.rs @@ -26,3 +26,26 @@ fn receiver(input: &[u8]) -> IResult<&[u8], &[u8]> { fn token(input: &[u8]) -> IResult<&[u8], &[u8]> { take_while(|i| i != b'\n' && i != b'\r')(input) } + +pub enum Chan { + /// # — network-global channel, available from any server in the network. + Global(ByteVec), + /// & — server-local channel, available only to connections to the same server. Rarely used in practice. + Local(ByteVec), +} + +fn chan(input: &[u8]) -> IResult<&[u8], Chan> { + fn chan_global(input: &[u8]) -> IResult<&[u8], Chan> { + let (input, _) = tag("#")(input)?; + let (input, name) = receiver(input)?; + Ok((input, Chan::Global(name.to_vec()))) + } + + fn chan_local(input: &[u8]) -> IResult<&[u8], Chan> { + let (input, _) = tag("&")(input)?; + let (input, name) = receiver(input)?; + Ok((input, Chan::Local(name.to_vec()))) + } + + alt((chan_global, chan_local))(input) +}