Skip to main content

max / pter

1.4 KB · 57 lines History Blame Raw
1 /// Normalize whitespace in the final markdown output.
2 ///
3 /// - Collapse runs of 3+ newlines into 2 (one blank line)
4 /// - Trim leading/trailing whitespace
5 /// - Remove trailing whitespace from each line
6 pub fn normalize(input: &str) -> String {
7 let mut result = String::with_capacity(input.len());
8 let mut consecutive_newlines = 0u32;
9
10 for ch in input.chars() {
11 if ch == '\n' {
12 consecutive_newlines += 1;
13 if consecutive_newlines <= 2 {
14 result.push('\n');
15 }
16 } else {
17 consecutive_newlines = 0;
18 result.push(ch);
19 }
20 }
21
22 // Trim trailing whitespace from each line
23 let lines: Vec<&str> = result.lines().map(|l| l.trim_end()).collect();
24 let joined = lines.join("\n");
25 joined.trim().to_string()
26 }
27
28 #[cfg(test)]
29 mod tests {
30 use super::*;
31
32 #[test]
33 fn collapse_excessive_newlines() {
34 assert_eq!(normalize("a\n\n\n\nb"), "a\n\nb");
35 }
36
37 #[test]
38 fn preserve_single_blank_line() {
39 assert_eq!(normalize("a\n\nb"), "a\n\nb");
40 }
41
42 #[test]
43 fn trim_trailing_whitespace() {
44 assert_eq!(normalize("hello \nworld "), "hello\nworld");
45 }
46
47 #[test]
48 fn trim_outer_whitespace() {
49 assert_eq!(normalize("\n\nhello\n\n"), "hello");
50 }
51
52 #[test]
53 fn empty_input() {
54 assert_eq!(normalize(""), "");
55 }
56 }
57