lavina/crates/proto-irc/src/lib.rs

154 lines
4.1 KiB
Rust
Raw Normal View History

2023-02-07 15:21:00 +00:00
//! Client-to-Server IRC protocol.
pub mod client;
mod prelude;
pub mod server;
#[cfg(test)]
mod testkit;
pub mod user;
2023-02-07 15:21:00 +00:00
2023-04-13 22:38:26 +00:00
use crate::prelude::Str;
2023-02-13 17:08:37 +00:00
2023-02-07 15:21:00 +00:00
use nom::{
branch::alt,
bytes::complete::{tag, take, take_while},
IResult,
};
2023-02-13 17:08:37 +00:00
use tokio::io::{AsyncWrite, AsyncWriteExt};
2023-02-07 15:21:00 +00:00
/// Single message tag value.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Tag {
2023-04-13 22:38:26 +00:00
key: Str,
2023-02-07 15:21:00 +00:00
value: Option<u8>,
}
2023-04-13 19:15:48 +00:00
fn receiver(input: &str) -> IResult<&str, &str> {
take_while(|i| i != '\n' && i != '\r' && i != ' ')(input)
2023-02-07 15:21:00 +00:00
}
2023-04-13 19:15:48 +00:00
fn token(input: &str) -> IResult<&str, &str> {
take_while(|i| i != '\n' && i != '\r')(input)
2023-02-07 15:21:00 +00:00
}
fn params(input: &str) -> IResult<&str, &str> {
take_while(|i| i != '\n' && i != '\r' && i != ':')(input)
}
2023-02-12 23:31:16 +00:00
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Chan {
/// #<name> — network-global channel, available from any server in the network.
2023-04-13 22:38:26 +00:00
Global(Str),
/// &<name> — server-local channel, available only to connections to the same server. Rarely used in practice.
2023-04-13 22:38:26 +00:00
Local(Str),
}
2023-02-13 17:08:37 +00:00
impl Chan {
2023-10-04 18:27:43 +00:00
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
2023-02-13 17:08:37 +00:00
match self {
Chan::Global(name) => {
writer.write_all(b"#").await?;
2023-04-13 19:15:48 +00:00
writer.write_all(name.as_bytes()).await?;
2023-02-13 17:08:37 +00:00
}
Chan::Local(name) => {
writer.write_all(b"&").await?;
2023-04-13 19:15:48 +00:00
writer.write_all(name.as_bytes()).await?;
2023-02-13 17:08:37 +00:00
}
}
Ok(())
}
}
2023-04-13 19:15:48 +00:00
fn chan(input: &str) -> IResult<&str, Chan> {
fn chan_global(input: &str) -> IResult<&str, Chan> {
let (input, _) = tag("#")(input)?;
let (input, name) = receiver(input)?;
2023-04-13 19:15:48 +00:00
Ok((input, Chan::Global(name.into())))
}
2023-04-13 19:15:48 +00:00
fn chan_local(input: &str) -> IResult<&str, Chan> {
let (input, _) = tag("&")(input)?;
let (input, name) = receiver(input)?;
2023-04-13 19:15:48 +00:00
Ok((input, Chan::Local(name.into())))
}
alt((chan_global, chan_local))(input)
}
2023-02-13 17:08:37 +00:00
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Recipient {
2023-04-13 22:38:26 +00:00
Nick(Str),
2023-02-13 17:08:37 +00:00
Chan(Chan),
}
impl Recipient {
2023-10-04 18:27:43 +00:00
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> std::io::Result<()> {
2023-02-13 17:08:37 +00:00
match self {
2023-04-13 19:15:48 +00:00
Recipient::Nick(nick) => writer.write_all(nick.as_bytes()).await?,
2023-02-13 17:08:37 +00:00
Recipient::Chan(chan) => chan.write_async(writer).await?,
}
Ok(())
}
}
2023-04-13 19:15:48 +00:00
fn recipient(input: &str) -> IResult<&str, Recipient> {
fn recipient_chan(input: &str) -> IResult<&str, Recipient> {
2023-02-13 17:08:37 +00:00
let (input, chan) = chan(input)?;
Ok((input, Recipient::Chan(chan)))
}
2023-04-13 19:15:48 +00:00
fn recipient_nick(input: &str) -> IResult<&str, Recipient> {
2023-02-13 17:08:37 +00:00
let (input, nick) = receiver(input)?;
2023-04-13 19:15:48 +00:00
Ok((input, Recipient::Nick(nick.into())))
2023-02-13 17:08:37 +00:00
}
alt((recipient_chan, recipient_nick))(input)
}
#[cfg(test)]
mod test {
use assert_matches::*;
use super::*;
use crate::testkit::*;
2023-02-13 17:08:37 +00:00
#[test]
fn test_chan_global() {
2023-04-13 19:15:48 +00:00
let input = "#testchan";
let expected = Chan::Global("testchan".into());
2023-02-13 17:08:37 +00:00
let result = chan(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
let mut bytes = vec![];
2024-03-20 18:59:15 +00:00
sync_future(expected.write_async(&mut bytes)).unwrap().unwrap();
2023-02-13 17:08:37 +00:00
2023-04-13 19:15:48 +00:00
assert_eq!(bytes.as_slice(), input.as_bytes());
2023-02-13 17:08:37 +00:00
}
#[test]
fn test_chan_local() {
2023-04-13 19:15:48 +00:00
let input = "&localchan";
let expected = Chan::Local("localchan".into());
2023-02-13 17:08:37 +00:00
let result = chan(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
let mut bytes = vec![];
2024-03-20 18:59:15 +00:00
sync_future(expected.write_async(&mut bytes)).unwrap().unwrap();
2023-02-13 17:08:37 +00:00
2023-04-13 19:15:48 +00:00
assert_eq!(bytes.as_slice(), input.as_bytes());
2023-02-13 17:08:37 +00:00
}
#[test]
fn test_recipient_user() {
2023-04-13 19:15:48 +00:00
let input = "User";
let expected = Recipient::Nick("User".into());
2023-02-13 17:08:37 +00:00
let result = recipient(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
let mut bytes = vec![];
2024-03-20 18:59:15 +00:00
sync_future(expected.write_async(&mut bytes)).unwrap().unwrap();
2023-02-13 17:08:37 +00:00
2023-04-13 19:15:48 +00:00
assert_eq!(bytes.as_slice(), input.as_bytes());
2023-02-13 17:08:37 +00:00
}
}