email_parser/parsing/
message.rs

1use crate::parsing::fields::{fields, Field};
2use crate::prelude::*;
3use std::borrow::Cow;
4
5pub fn line(input: &[u8]) -> Res<Cow<str>> {
6    let max_idx = std::cmp::min(input.len(), 998);
7
8    // index cannot be out of range so no need to check
9    unsafe {
10        for i in 0..max_idx {
11            if !is_text(*input.get_unchecked(i)) {
12                return Ok((
13                    input.get_unchecked(i..),
14                    from_slice(input.get_unchecked(..i)),
15                ));
16            }
17        }
18
19        Ok((
20            input.get_unchecked(max_idx..),
21            from_slice(input.get_unchecked(..max_idx)),
22        ))
23    }
24}
25
26pub fn check_line(input: &[u8]) -> Res<()> {
27    let max_idx = std::cmp::min(input.len(), 998);
28
29    // index cannot be out of range so no need to check
30    unsafe {
31        for i in 0..max_idx {
32            if !is_text(*input.get_unchecked(i)) {
33                return Ok((input.get_unchecked(i..), ()));
34            }
35        }
36
37        Ok((input.get_unchecked(max_idx..), ()))
38    }
39}
40
41pub fn body_lines(input: &[u8]) -> Result<Vec<Cow<str>>, Error> {
42    if input.is_empty() {
43        return Ok(Vec::new());
44    }
45    let (mut input, ()) = tag(input, b"\r\n")?;
46
47    let mut lines = Vec::new();
48    loop {
49        let (new_input, new_line) = line(input)?;
50        match tag(new_input, b"\r\n") {
51            Ok((new_input, ())) => input = new_input,
52            Err(e) => {
53                if new_input.is_empty() {
54                    lines.push(new_line);
55                    break;
56                } else {
57                    return Err(e);
58                }
59            }
60        }
61        lines.push(new_line);
62    }
63
64    Ok(lines)
65}
66
67pub fn body(input: &[u8]) -> Result<Option<Cow<str>>, Error> {
68    if input.is_empty() {
69        return Ok(None);
70    }
71
72    let (mut new_input, ()) = tag(input, b"\r\n")?;
73
74    loop {
75        let (new_input2, ()) = check_line(new_input)?;
76        match tag(new_input2, b"\r\n") {
77            Ok((new_input2, ())) => new_input = new_input2,
78            Err(e) => {
79                if new_input2.is_empty() {
80                    break;
81                } else {
82                    return Err(e);
83                }
84            }
85        }
86    }
87
88    Ok(Some(unsafe {
89        // there is a least 2 characters
90        from_slice(input.get_unchecked(2..))
91    }))
92}
93
94#[cfg(not(feature = "mime"))]
95pub fn parse_message(input: &[u8]) -> Result<(Vec<Field>, Option<Cow<str>>), Error> {
96    let (input, fields) = fields(input)?;
97    let body = body(input)?;
98
99    Ok((fields, body))
100}
101
102#[cfg(feature = "mime")]
103pub fn parse_message(input: &[u8]) -> Result<(Vec<Field>, Option<&[u8]>), Error> {
104    let (input, fields) = fields(input)?;
105
106    if input.is_empty() {
107        return Ok((fields, None));
108    }
109
110    let (new_input, ()) = tag(input, b"\r\n")?;
111
112    Ok((fields, Some(new_input)))
113}
114
115#[cfg(test)]
116mod test {
117    use super::*;
118
119    #[test]
120    fn test_body() {
121        assert_eq!(
122            line(b"This is a line\r\nAnd this is a second line")
123                .unwrap()
124                .1,
125            "This is a line"
126        );
127        assert_eq!(
128            body_lines(b"\r\nThis is a line\r\nAnd this is a second line")
129                .unwrap()
130                .len(),
131            2
132        );
133        assert_eq!(
134            body(b"\r\nThis is a line\r\nAnd this is a second line")
135                .unwrap()
136                .unwrap(),
137            "This is a line\r\nAnd this is a second line"
138        );
139    }
140
141    #[test]
142    fn test_full_message() {
143        //println!("{:#?}", parse_message(include_bytes!("../../mail.txt")).unwrap());
144    }
145}