//! Client-to-Server IRC protocol. pub mod client; pub mod server; use std::io::Result; use nom::{ branch::alt, bytes::complete::{tag, take, take_while}, IResult, }; use tokio::io::{AsyncWrite, AsyncWriteExt}; type ByteVec = Vec; /// Single message tag value. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Tag { key: ByteVec, value: Option, } 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) } #[derive(Clone, Debug, PartialEq, Eq)] pub enum Chan { /// # — network-global channel, available from any server in the network. Global(ByteVec), /// & — server-local channel, available only to connections to the same server. Rarely used in practice. Local(ByteVec), } 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) } #[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); } }