mail_headers_ng/header_components/utils/
text_partition.rs

1use internals::grammar::{is_vchar, is_ws};
2use internals::MailType;
3
4
5#[derive(Copy, Clone, Debug, Fail, PartialEq, Eq, Hash)]
6#[fail(display = "text contained control characters")]
7pub struct PartitionError;
8
9#[derive(Copy, Clone)]
10pub enum Partition<'a> {
11    //from -> to the start of the next block
12    SPACE(&'a str),
13    VCHAR(&'a str)
14}
15
16#[derive(Clone, Copy, PartialEq)]
17enum Type { SPACE, VCHAR }
18
19pub fn partition<'a>( text: &'a str ) -> Result<Vec<Partition<'a>>, PartitionError> {
20    use self::Type::*;
21
22    if text.len() == 0 {
23        return Ok( Vec::new() );
24    }
25
26    // unwrap is ok, as we return earlier if len == 0
27    let start_with_vchar = is_vchar( text.chars().next().unwrap(), MailType::Internationalized);
28
29    let mut partitions =  Vec::new();
30    let mut current_type = if start_with_vchar { VCHAR } else { SPACE };
31
32    let mut start_of_current = 0;
33    for (idx, ch) in text.char_indices() {
34        if is_vchar(ch, MailType::Internationalized) {
35            if current_type == SPACE {
36                // idx is the start index of the current char, with is the
37                // (exclusive) end index of the previous char which is the
38                // last char of the Partition we want to push
39                partitions.push(Partition::SPACE(&text[start_of_current..idx]));
40                start_of_current = idx;
41                current_type = VCHAR
42            }
43        } else if is_ws(ch) || ch == '\r' || ch == '\n' {
44            if current_type == VCHAR {
45                partitions.push(Partition::VCHAR(&text[start_of_current..idx]));
46                start_of_current = idx;
47                current_type = SPACE
48            }
49        } else {
50            //TODO look into this error case and especially PartitionError's Display impl
51            return Err(PartitionError);
52        }
53    }
54
55
56    partitions.push( match current_type {
57        SPACE => Partition::SPACE(&text[start_of_current..]),
58        VCHAR => Partition::VCHAR(&text[start_of_current..])
59    } );
60
61    Ok( partitions )
62}