2023-02-09 19:26:05 +00:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
/// Client-to-server command.
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub enum ClientMessage {
|
|
|
|
/// CAP. Capability-related commands.
|
|
|
|
Capability {
|
|
|
|
subcommand: CapabilitySubcommand,
|
|
|
|
},
|
2023-02-10 10:46:10 +00:00
|
|
|
/// PING <token>
|
2023-02-09 19:26:05 +00:00
|
|
|
Ping {
|
|
|
|
token: ByteVec,
|
|
|
|
},
|
2023-02-10 10:46:10 +00:00
|
|
|
/// PONG <token>
|
2023-02-09 19:26:05 +00:00
|
|
|
Pong {
|
|
|
|
token: ByteVec,
|
|
|
|
},
|
2023-02-10 10:46:10 +00:00
|
|
|
/// NICK <name>
|
|
|
|
Nick {
|
|
|
|
nickname: ByteVec,
|
|
|
|
},
|
|
|
|
User {
|
|
|
|
username: ByteVec,
|
|
|
|
realname: ByteVec,
|
|
|
|
},
|
|
|
|
Quit {
|
|
|
|
reason: ByteVec,
|
|
|
|
},
|
2023-02-09 19:26:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn client_message(input: &[u8]) -> IResult<&[u8], ClientMessage> {
|
|
|
|
alt((
|
|
|
|
client_message_capability,
|
|
|
|
client_message_ping,
|
|
|
|
client_message_pong,
|
2023-02-10 10:46:10 +00:00
|
|
|
client_message_nick,
|
|
|
|
client_message_user,
|
|
|
|
client_message_quit,
|
2023-02-09 19:26:05 +00:00
|
|
|
))(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(),
|
|
|
|
},
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2023-02-10 10:46:10 +00:00
|
|
|
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(),
|
|
|
|
},
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2023-02-09 19:26:05 +00:00
|
|
|
#[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(),
|
|
|
|
};
|
|
|
|
|
2023-02-10 10:46:10 +00:00
|
|
|
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(),
|
|
|
|
};
|
|
|
|
|
2023-02-09 19:26:05 +00:00
|
|
|
let result = client_message(input);
|
|
|
|
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
|
|
|
|
}
|
|
|
|
}
|