summaryrefslogtreecommitdiffstats
path: root/headers/src/header_components/utils/text_partition.rs
blob: 59047b5b364900aca0e52092c0a92fe931b28c61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
use internals::grammar::{is_vchar, is_ws};
use internals::MailType;


#[derive(Copy, Clone, Debug, Fail, PartialEq, Eq, Hash)]
#[fail(display = "text contained control characters")]
pub struct PartitionError;

#[derive(Copy, Clone)]
pub enum Partition<'a> {
    //from -> to the start of the next block
    SPACE(&'a str),
    VCHAR(&'a str)
}

#[derive(Clone, Copy, PartialEq)]
enum Type { SPACE, VCHAR }

pub fn partition<'a>( text: &'a str ) -> Result<Vec<Partition<'a>>, PartitionError> {
    use self::Type::*;

    if text.len() == 0 {
        return Ok( Vec::new() );
    }

    // unwrap is ok, as we return earlier if len == 0
    let start_with_vchar = is_vchar( text.chars().next().unwrap(), MailType::Internationalized);

    let mut partitions =  Vec::new();
    let mut current_type = if start_with_vchar { VCHAR } else { SPACE };

    let mut start_of_current = 0;
    for (idx, ch) in text.char_indices() {
        if is_vchar(ch, MailType::Internationalized) {
            if current_type == SPACE {
                // idx is the start index of the current char, with is the
                // (exclusive) end index of the previous char which is the
                // last char of the Partition we want to push
                partitions.push(Partition::SPACE(&text[start_of_current..idx]));
                start_of_current = idx;
                current_type = VCHAR
            }
        } else if is_ws(ch) || ch == '\r' || ch == '\n' {
            if current_type == VCHAR {
                partitions.push(Partition::VCHAR(&text[start_of_current..idx]));
                start_of_current = idx;
                current_type = SPACE
            }
        } else {
            //TODO look into this error case and especially PartitionError's Display impl
            return Err(PartitionError);
        }
    }


    partitions.push( match current_type {
        SPACE => Partition::SPACE(&text[start_of_current..]),
        VCHAR => Partition::VCHAR(&text[start_of_current..])
    } );

    Ok( partitions )
}