2023-02-07 15:21:00 +00:00
|
|
|
//! Client-to-Server IRC protocol.
|
2023-02-09 19:26:05 +00:00
|
|
|
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-10 10:46:10 +00:00
|
|
|
|
2023-02-12 23:31:16 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
2023-02-10 10:46:10 +00:00
|
|
|
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(())
|
|
|
|
}
|
|
|
|
}
|
2023-02-10 10:46:10 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|