tellem/
codec.rs

1use std::io;
2
3use bytes::{
4    BufMut,
5    BytesMut,
6};
7use either::Either;
8use thiserror::Error;
9use tokio_util::codec::{
10    Decoder,
11    Encoder,
12};
13
14use crate::{
15    event::Event,
16    op::Cmd,
17    parser::{
18        self,
19        Parser,
20    },
21    util::Escape,
22};
23
24/// Errors arising from the telnet codec
25#[derive(Debug, Error)]
26pub enum Error {
27    /// An error occurred while parsing
28    #[error("parse error: {0}")]
29    Parse(#[from] parser::Error),
30    /// An error occurred in the underlying IO stream
31    #[error("io error: {0}")]
32    Io(#[from] io::Error),
33}
34
35impl Decoder for Parser {
36    type Error = Error;
37    type Item = Event;
38
39    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
40        Ok(loop {
41            if buf.is_empty() {
42                break None;
43            }
44            match self.parse(buf)? {
45                Either::Left(true) => continue,
46                Either::Left(false) => break None,
47                Either::Right(item) => break item.into(),
48            }
49        })
50    }
51}
52
53impl Encoder<Event> for Parser {
54    type Error = io::Error;
55
56    fn encode(&mut self, item: Event, dst: &mut BytesMut) -> Result<(), Self::Error> {
57        match item {
58            Event::Data(bytes) => {
59                bytes.escape_to(dst);
60            }
61            Event::Cmd(cmd) => {
62                dst.put_u8(Cmd::IAC.into());
63                dst.put_u8(cmd.into());
64            }
65            Event::Negotiation(cmd, opt) => {
66                dst.put_u8(Cmd::IAC.into());
67                dst.put_u8(cmd.into());
68                dst.put_u8(opt.into());
69            }
70            Event::Subnegotiation(opt, params) => {
71                dst.put_u8(Cmd::IAC.into());
72                dst.put_u8(Cmd::SB.into());
73                dst.put_u8(opt.into());
74                params.escape_to(dst);
75                dst.put_u8(Cmd::IAC.into());
76                dst.put_u8(Cmd::SE.into());
77            }
78        }
79
80        Ok(())
81    }
82}