email_rs/
email.rs

1use crate::header_value_parser::{create_header, EmailHeader};
2use std::collections::HashMap;
3/// Email represents an Email object and contains all the properties and data
4/// related to an email
5#[derive(Debug)]
6pub struct Email {
7    /// All the Headers for top level Email.
8    pub headers: HashMap<String, EmailHeader>,
9
10    /// body of the email is going to be stored as a string for now since we are
11    /// going to parse only simple emails.
12    pub body: EmailBody,
13
14    /// Children are child Email objects in case of multipart emails.
15    pub children: Vec<Email>,
16}
17
18/// For simplicity Email's body is now just going to be string.
19type EmailBody = String;
20
21impl Email {
22    /// generate an Email from string object.
23    pub fn from_str(s: String) -> Email {
24        let mut allheaders = HashMap::new();
25
26        let val: Vec<&str> = s.split("\n\n").collect();
27        println!("{:?}", val);
28        let mut headers = Some(val[0]);
29
30        while let Some(_) = headers {
31            match Email::get_one_header(headers.unwrap()) {
32                (Some(headerval), rest) => {
33                    let key_val: Vec<&str> = headerval.rsplitn(2, ':').collect();
34                    allheaders.insert(
35                        String::from(key_val[1].to_lowercase()),
36                        create_header(key_val[1], key_val[0].trim_start()),
37                    );
38                    headers = rest;
39                }
40                _ => break,
41            }
42        }
43
44        if let Some(EmailHeader::ContentType {
45            maintype,
46            subtype,
47            value,
48        }) = allheaders.get("content-type")
49        {
50            if maintype == "multipart" {
51                println!("Found a multipart email.")
52            }
53        }
54
55        Email {
56            headers: allheaders,
57            body: val[1].to_string(),
58            children: vec![],
59        }
60    }
61
62    /// get_one_header tries to parse one header line from the provided buffer
63    /// and returns the rest back.
64    fn get_one_header(_s: &str) -> (Option<String>, Option<&str>) {
65        let mut header_line = String::new();
66        let mut last = 0;
67        let bytes = _s.as_bytes();
68        for (i, &x) in bytes.iter().enumerate() {
69            last = i;
70            if x == b'\n' {
71                if bytes[i + 1] == b' ' {
72                    // If the next line starts with a whitespace, we continue
73                    // parsing as a part of this same header.
74                    continue;
75                } else {
76                    break;
77                }
78            } else {
79                header_line.push(x as char);
80            }
81        }
82
83        let mut rest = Some(&_s[last + 1..]);
84        if last + 1 == _s.len() {
85            rest = None;
86        }
87        (Some(header_line), rest)
88    }
89
90    /// generate an Email from raw bytes.
91    fn from_bytes() -> Email {
92        unimplemented!();
93    }
94
95    /// generate an email from a file path.
96    fn from_file() -> Email {
97        unimplemented!();
98    }
99
100    /// Create a new email.
101    fn new() -> Email {
102        unimplemented!();
103    }
104}
105
106#[test]
107fn test_get_one_simple_header() {
108    let headers = "From: Someone
109To: Person
110Date: Today";
111    assert_eq!(
112        Email::get_one_header(headers),
113        (
114            Some("From: Someone".to_string()),
115            Some("To: Person\nDate: Today")
116        )
117    )
118}
119
120#[test]
121fn test_get_one_multiline_header() {
122    let headers = "From: acomplexheader
123Subject: This is a complex header which goes to
124 2nd line identified by whitespace at the
125 start of each next line of header.";
126    let (header, rest) = Email::get_one_header(headers);
127    assert_eq!(header, Some("From: acomplexheader".to_string()));
128    assert_eq!(rest.is_some(), true);
129    let (header, rest) = Email::get_one_header(rest.unwrap());
130    assert_eq!(
131        header,
132        Some(
133            "Subject: This is a complex \
134             header which goes to 2nd line identified by whitespace at \
135             the start of each next line of header."
136                .to_string()
137        )
138    );
139    assert_eq!(rest, None);
140}