1use bstr::{BStr, ByteVec};
2
3pub const SOFT_WIDTH: usize = 75;
4pub const HARD_WIDTH: usize = 900;
5
6pub fn wrap(value: &str) -> String {
7 String::from_utf8(wrap_impl(value, SOFT_WIDTH, HARD_WIDTH)).expect("utf8-in, utf8-out")
8}
9
10pub fn wrap_bytes(value: impl AsRef<BStr>) -> Vec<u8> {
11 wrap_impl(value, SOFT_WIDTH, HARD_WIDTH)
12}
13
14pub fn wrap_impl(value: impl AsRef<BStr>, soft_width: usize, hard_width: usize) -> Vec<u8> {
20 let value: &BStr = value.as_ref();
21 let mut result: Vec<u8> = vec![];
22 let mut line: Vec<u8> = vec![];
23
24 for word in value.split(|&b| b.is_ascii_whitespace()) {
25 if word.len() == 0 {
26 continue;
27 }
28 if line.len() + word.len() < soft_width {
29 if !line.is_empty() {
30 line.push(b' ');
31 }
32 line.push_str(word);
33 continue;
34 }
35
36 if !line.is_empty() {
40 if !result.is_empty() {
41 result.push(b'\t');
43 }
44 result.push_str(&line);
45 result.push_str("\r\n");
46 line.clear();
47 }
48
49 if word.len() <= hard_width {
51 line.push_str(word);
52 } else {
53 for &c in word.iter() {
54 line.push(c);
55 if line.len() >= hard_width {
56 if !result.is_empty() {
57 result.push(b'\t');
58 }
59 result.push_str(&line);
60 result.push_str("\r\n");
61 line.clear();
62 continue;
63 }
64 }
65 }
66 }
67
68 if !line.is_empty() {
69 if !result.is_empty() {
70 result.push(b'\t');
71 }
72 result.push_str(&line);
73 }
74
75 result
76}
77
78#[cfg(test)]
79mod test {
80 use super::*;
81
82 #[test]
83 fn wrapping() {
84 for (input, expect) in [
85 ("foo", "foo"),
86 ("hi there", "hi there"),
87 ("hello world", "hello\r\n\tworld"),
88 ("hello world ", "hello\r\n\tworld"),
89 (
90 "hello world foo bar baz woot woot",
91 "hello\r\n\tworld foo\r\n\tbar baz\r\n\twoot woot",
92 ),
93 (
94 "hi there breakmepleaseIamtoolong",
95 "hi there\r\n\tbreakmepleaseIa\r\n\tmtoolong",
96 ),
97 ] {
98 let wrapped = wrap_impl(input, 10, 15);
99 k9::assert_equal!(
100 wrapped,
101 expect.as_bytes(),
102 "input: '{input}' should produce '{expect}'"
103 );
104 }
105 }
106}