lavina/src/protos/irc/client.rs

301 lines
8.0 KiB
Rust
Raw Normal View History

use super::*;
/// Client-to-server command.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ClientMessage {
/// CAP. Capability-related commands.
Capability {
subcommand: CapabilitySubcommand,
},
/// PING <token>
Ping {
token: ByteVec,
},
/// PONG <token>
Pong {
token: ByteVec,
},
2023-02-13 17:08:37 +00:00
/// NICK <nickname>
Nick {
nickname: ByteVec,
},
2023-02-13 17:08:37 +00:00
/// USER <username> 0 * :<realname>
User {
username: ByteVec,
realname: ByteVec,
},
2023-02-13 17:08:37 +00:00
/// JOIN <chan>
2023-02-12 23:31:16 +00:00
Join(Chan),
/// MODE <target>
Mode(Chan), // TODO support not only chan
/// WHO <target>
Who(Chan), // TODO support not only chan
/// TOPIC <chan> :<topic>
Topic {
chan: Chan,
topic: ByteVec,
},
Part {
chan: Chan,
message: ByteVec,
},
2023-02-13 17:08:37 +00:00
/// PRIVMSG <target> :<msg>
PrivateMessage {
recipient: Recipient,
body: ByteVec,
},
2023-02-13 17:08:37 +00:00
/// QUIT :<reason>
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,
2023-02-12 23:31:16 +00:00
client_message_join,
client_message_mode,
client_message_who,
client_message_topic,
client_message_part,
2023-02-13 17:08:37 +00:00
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(),
},
))
}
2023-02-12 23:31:16 +00:00
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 }))
}
2023-02-13 17:08:37 +00:00
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));
}
}