forked from lavina/lavina
1
0
Fork 0
lavina/src/protos/irc/mod.rs

154 lines
4.0 KiB
Rust
Raw Normal View History

2023-02-07 15:21:00 +00:00
//! Client-to-Server IRC protocol.
pub mod client;
pub mod server;
2023-02-07 15:21:00 +00:00
2023-02-13 17:08:37 +00:00
use std::io::Result;
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
type ByteVec = Vec<u8>;
/// Single message tag value.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Tag {
key: ByteVec,
value: Option<u8>,
}
fn receiver(input: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(|i| i != b'\n' && i != b'\r' && i != b' ')(input)
}
fn token(input: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(|i| i != b'\n' && i != b'\r')(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.
Global(ByteVec),
/// &<name> — server-local channel, available only to connections to the same server. Rarely used in practice.
Local(ByteVec),
}
2023-02-13 17:08:37 +00:00
impl Chan {
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> {
match self {
Chan::Global(name) => {
writer.write_all(b"#").await?;
writer.write_all(&name).await?;
}
Chan::Local(name) => {
writer.write_all(b"&").await?;
writer.write_all(&name).await?;
}
}
Ok(())
}
}
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)
}
2023-02-13 17:08:37 +00:00
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Recipient {
Nick(ByteVec),
Chan(Chan),
}
impl Recipient {
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> {
match self {
Recipient::Nick(nick) => writer.write_all(&nick).await?,
Recipient::Chan(chan) => chan.write_async(writer).await?,
}
Ok(())
}
}
fn recipient(input: &[u8]) -> IResult<&[u8], Recipient> {
fn recipient_chan(input: &[u8]) -> IResult<&[u8], Recipient> {
let (input, chan) = chan(input)?;
Ok((input, Recipient::Chan(chan)))
}
fn recipient_nick(input: &[u8]) -> IResult<&[u8], Recipient> {
let (input, nick) = receiver(input)?;
Ok((input, Recipient::Nick(nick.to_vec())))
}
alt((recipient_chan, recipient_nick))(input)
}
#[cfg(test)]
mod test {
use assert_matches::*;
use super::*;
use crate::util::testkit::*;
#[test]
fn test_chan_global() {
let input = b"#testchan";
let expected = Chan::Global(b"testchan".to_vec());
let result = chan(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
let mut bytes = vec![];
sync_future(expected.write_async(&mut bytes))
.unwrap()
.unwrap();
assert_eq!(bytes.as_slice(), input);
}
#[test]
fn test_chan_local() {
let input = b"&localchan";
let expected = Chan::Local(b"localchan".to_vec());
let result = chan(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
let mut bytes = vec![];
sync_future(expected.write_async(&mut bytes))
.unwrap()
.unwrap();
assert_eq!(bytes.as_slice(), input);
}
#[test]
fn test_recipient_user() {
let input = b"User";
let expected = Recipient::Nick(b"User".to_vec());
let result = recipient(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
let mut bytes = vec![];
sync_future(expected.write_async(&mut bytes))
.unwrap()
.unwrap();
assert_eq!(bytes.as_slice(), input);
}
}