tokio_imap/
proto.rs

1use std::io;
2use std::mem;
3
4use bytes::{BufMut, Bytes, BytesMut};
5use nom::{self, Needed};
6use tokio_util::codec::{Decoder, Encoder, Framed};
7
8use imap_proto;
9use imap_proto::types::{Request, RequestId, Response};
10
11pub struct ImapCodec {
12    decode_need_message_bytes: usize,
13}
14
15impl Default for ImapCodec {
16    fn default() -> Self {
17        Self {
18            decode_need_message_bytes: 0,
19        }
20    }
21}
22
23impl<'a> Decoder for ImapCodec {
24    type Item = ResponseData;
25    type Error = io::Error;
26    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, io::Error> {
27        if self.decode_need_message_bytes > buf.len() {
28            return Ok(None);
29        }
30        let (response, rsp_len) = match imap_proto::parse_response(buf) {
31            Ok((remaining, response)) => {
32                // This SHOULD be acceptable/safe: BytesMut storage memory is
33                // allocated on the heap and should not move. It will not be
34                // freed as long as we keep a reference alive, which we do
35                // by retaining a reference to the split buffer, below.
36                let response = unsafe { mem::transmute(response) };
37                (response, buf.len() - remaining.len())
38            }
39            Err(nom::Err::Incomplete(Needed::Size(min))) => {
40                self.decode_need_message_bytes = min;
41                return Ok(None);
42            }
43            Err(nom::Err::Incomplete(_)) => {
44                return Ok(None);
45            }
46            Err(nom::Err::Error((_input, err_kind)))
47            | Err(nom::Err::Failure((_input, err_kind))) => {
48                return Err(io::Error::new(
49                    io::ErrorKind::Other,
50                    format!("{:?} during parsing of {:?}", err_kind, buf),
51                ));
52            }
53        };
54        let raw = buf.split_to(rsp_len).freeze();
55        self.decode_need_message_bytes = 0;
56        Ok(Some(ResponseData { raw, response }))
57    }
58}
59
60impl Encoder for ImapCodec {
61    type Item = Request;
62    type Error = io::Error;
63    fn encode(&mut self, msg: Self::Item, dst: &mut BytesMut) -> Result<(), io::Error> {
64        dst.put(msg.0.as_bytes());
65        dst.put_u8(b' ');
66        dst.put_slice(&msg.1);
67        dst.put("\r\n".as_bytes());
68        Ok(())
69    }
70}
71
72#[derive(Debug)]
73pub struct ResponseData {
74    raw: Bytes,
75    // This reference is really scoped to the lifetime of the `raw`
76    // member, but unfortunately Rust does not allow that yet. It
77    // is transmuted to `'static` by the `Decoder`, instead, and
78    // references returned to callers of `ResponseData` are limited
79    // to the lifetime of the `ResponseData` struct.
80    //
81    // `raw` is never mutated during the lifetime of `ResponseData`,
82    // and `Response` does not not implement any specific drop glue.
83    response: Response<'static>,
84}
85
86impl ResponseData {
87    pub fn request_id(&self) -> Option<&RequestId> {
88        match self.response {
89            Response::Done { ref tag, .. } => Some(tag),
90            _ => None,
91        }
92    }
93    pub fn parsed<'a>(&'a self) -> &'a Response {
94        &self.response
95    }
96}
97
98pub type ImapTransport<T> = Framed<T, ImapCodec>;