mail_parser/parsers/
mod.rs1use 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}