mail_parser/parsers/
mod.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use std::{iter::Peekable, ops::Range, slice::Iter};
8
9pub mod fields;
10pub mod header;
11pub mod message;
12pub mod mime;
13pub mod preview;
14
15pub struct MessageStream<'x> {
16    data: &'x [u8],
17    iter: Peekable<Iter<'x, u8>>,
18    pos: usize,
19    restore_pos: usize,
20}
21
22impl<'x> MessageStream<'x> {
23    pub fn new(data: &'x [u8]) -> MessageStream<'x> {
24        MessageStream {
25            data,
26            iter: data.iter().peekable(),
27            pos: 0,
28            restore_pos: 0,
29        }
30    }
31
32    #[inline(always)]
33    pub fn peek(&mut self) -> Option<&&u8> {
34        self.iter.peek()
35    }
36
37    #[inline(always)]
38    pub fn offset(&self) -> usize {
39        std::cmp::min(self.pos, self.data.len())
40    }
41
42    #[inline(always)]
43    pub fn remaining(&self) -> usize {
44        self.data.len() - self.offset()
45    }
46
47    #[inline(always)]
48    pub fn checkpoint(&mut self) {
49        self.restore_pos = self.offset();
50    }
51
52    #[inline(always)]
53    pub fn restore(&mut self) {
54        self.iter = self.data[self.restore_pos..].iter().peekable();
55        self.pos = self.restore_pos;
56        self.restore_pos = 0;
57    }
58
59    #[inline(always)]
60    pub fn reset(&mut self) {
61        self.restore_pos = 0;
62    }
63
64    #[inline(always)]
65    pub fn peek_bytes(&self, len: usize) -> Option<&[u8]> {
66        let pos = self.offset();
67        self.data.get(pos..pos + len)
68    }
69
70    #[inline(always)]
71    pub fn peek_char(&mut self, ch: u8) -> bool {
72        matches!(self.peek(), Some(&&ch_) if ch_ == ch)
73    }
74
75    #[inline(always)]
76    pub fn skip_bytes(&mut self, len: usize) {
77        self.pos += len;
78        self.iter = self.data[self.pos..].iter().peekable();
79    }
80
81    #[inline(always)]
82    pub fn try_skip(&mut self, bytes: &[u8]) -> bool {
83        if self.peek_bytes(bytes.len()) == Some(bytes) {
84            self.skip_bytes(bytes.len());
85            true
86        } else {
87            false
88        }
89    }
90
91    #[inline(always)]
92    pub fn try_skip_char(&mut self, ch: u8) -> bool {
93        if self.peek_char(ch) {
94            self.next();
95            true
96        } else {
97            false
98        }
99    }
100
101    #[inline(always)]
102    pub fn bytes(&self, range: Range<usize>) -> &'x [u8] {
103        &self.data[range]
104    }
105
106    #[inline(always)]
107    pub fn seek_end(&mut self) {
108        self.pos = self.data.len();
109        self.iter = [][..].iter().peekable();
110    }
111
112    #[inline(always)]
113    pub fn next_is_space(&mut self) -> bool {
114        matches!(self.next(), Some(b' ' | b'\t'))
115    }
116
117    #[inline(always)]
118    pub fn peek_next_is_space(&mut self) -> bool {
119        matches!(self.peek(), Some(b' ' | b'\t'))
120    }
121
122    #[inline(always)]
123    pub fn try_next_is_space(&mut self) -> bool {
124        if self.peek_next_is_space() {
125            self.next();
126            true
127        } else {
128            false
129        }
130    }
131
132    #[allow(clippy::len_without_is_empty)]
133    #[inline(always)]
134    pub fn len(&self) -> usize {
135        self.data.len()
136    }
137
138    #[inline(always)]
139    pub fn is_eof(&mut self) -> bool {
140        self.iter.peek().is_none()
141    }
142}
143
144impl<'x> Iterator for MessageStream<'x> {
145    type Item = &'x u8;
146
147    #[inline(always)]
148    fn next(&mut self) -> Option<Self::Item> {
149        self.pos += 1;
150        self.iter.next()
151    }
152}