irc_async/proto/
irc.rs

1//! Implementation of IRC codec for Tokio.
2use bytes::BytesMut;
3use tokio_util::codec::{Decoder, Encoder, LinesCodec};
4
5use super::errors::IrcError;
6use super::message::Message;
7
8/// An IRC codec built around an inner codec.
9#[derive(Default)]
10pub struct IrcCodec {
11    inner: LinesCodec,
12}
13
14impl IrcCodec {
15    /// Sanitizes the input string by cutting up to (and including) the first occurence of a line
16    /// terminiating phrase (`\r\n`, `\r`, or `\n`). This is used in sending messages back to
17    /// prevent the injection of additional commands.
18    pub(crate) fn sanitize(mut data: String) -> String {
19        // n.b. ordering matters here to prefer "\r\n" over "\r"
20        if let Some((pos, len)) = ["\r\n", "\r", "\n"]
21            .iter()
22            .flat_map(|needle| data.find(needle).map(|pos| (pos, needle.len())))
23            .min_by_key(|&(pos, _)| pos)
24        {
25            data.truncate(pos + len);
26        }
27        data
28    }
29}
30
31impl Decoder for IrcCodec {
32    type Item = Message;
33    type Error = IrcError;
34
35    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Message>, Self::Error> {
36        let result = self
37            .inner
38            .decode(src)
39            .map_err(IrcError::from)
40            .and_then(|res| res.map_or(Ok(None), |msg| msg.parse::<Message>().map(Some)));
41        println!(" << {:?}", result);
42        result
43    }
44}
45
46impl Encoder for IrcCodec {
47    type Item = Message;
48    type Error = IrcError;
49
50    fn encode(&mut self, msg: Message, dst: &mut BytesMut) -> Result<(), Self::Error> {
51        println!(">> {:?}", msg);
52        self.inner
53            .encode(IrcCodec::sanitize(msg.to_string()), dst)
54            .map_err(IrcError::from)
55    }
56}