mailparsing/
textwrap.rs

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
pub fn wrap(value: &str) -> String {
    const SOFT_WIDTH: usize = 75;
    const HARD_WIDTH: usize = 1000;
    wrap_impl(value, SOFT_WIDTH, HARD_WIDTH)
}

/// We can't use textwrap::fill here because it will prefer to break
/// a line rather than finding stuff that fits.  We use a simple
/// algorithm that tries to fill up to the desired width, allowing
/// for overflow if there is a word that is too long to fit in
/// the header, but breaking after a hard limit threshold.
fn wrap_impl(value: &str, soft_width: usize, hard_width: usize) -> String {
    let mut result = String::new();
    let mut line = String::new();

    for word in value.split_ascii_whitespace() {
        if line.len() + word.len() + 1 <= soft_width {
            if !line.is_empty() {
                line.push(' ');
            }
            line.push_str(word);
            continue;
        }

        // Need to wrap.

        // Accumulate line so far, if any
        if !line.is_empty() {
            if !result.is_empty() {
                // There's an existing line, start a new one, indented
                result.push('\t');
            }
            result.push_str(&line);
            result.push_str("\r\n");
            line.clear();
        }

        // build out a line from the characters of this one
        if word.len() <= hard_width {
            line.push_str(word);
        } else {
            for c in word.chars() {
                line.push(c);
                if line.len() >= hard_width {
                    if !result.is_empty() {
                        result.push('\t');
                    }
                    result.push_str(&line);
                    result.push_str("\r\n");
                    line.clear();
                    continue;
                }
            }
        }
    }

    if !line.is_empty() {
        if !result.is_empty() {
            result.push('\t');
        }
        result.push_str(&line);
    }

    result
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn wrapping() {
        for (input, expect) in [
            ("foo", "foo"),
            ("hi there", "hi there"),
            ("hello world", "hello\r\n\tworld"),
            (
                "hello world foo bar baz woot woot",
                "hello\r\n\tworld foo\r\n\tbar baz\r\n\twoot woot",
            ),
            (
                "hi there breakmepleaseIamtoolong",
                "hi there\r\n\tbreakmepleaseIa\r\n\tmtoolong",
            ),
        ] {
            let wrapped = wrap_impl(input, 10, 15);
            k9::assert_equal!(
                wrapped,
                expect,
                "input: '{input}' should produce '{expect}'"
            );
        }
    }
}