sup_xml_core/xsd/
whitespace.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum WhitespaceMode {
10 Preserve,
12 Replace,
14 Collapse,
17}
18
19impl WhitespaceMode {
20 pub fn apply<'a>(self, s: &'a str) -> std::borrow::Cow<'a, str> {
22 use std::borrow::Cow;
23 match self {
24 WhitespaceMode::Preserve => Cow::Borrowed(s),
25 WhitespaceMode::Replace => {
26 if s.bytes().any(|b| matches!(b, b'\t' | b'\n' | b'\r')) {
27 let out: String = s.chars()
28 .map(|c| if matches!(c, '\t' | '\n' | '\r') { ' ' } else { c })
29 .collect();
30 Cow::Owned(out)
31 } else {
32 Cow::Borrowed(s)
33 }
34 }
35 WhitespaceMode::Collapse => {
36 let needs_work = s.bytes().any(|b| matches!(b, b'\t' | b'\n' | b'\r'))
38 || s.contains(" ")
39 || s.starts_with(' ')
40 || s.ends_with(' ');
41 if !needs_work {
42 return Cow::Borrowed(s);
43 }
44 let mut out = String::with_capacity(s.len());
45 let mut prev_space = true; for c in s.chars() {
47 let is_ws = matches!(c, ' ' | '\t' | '\n' | '\r');
48 if is_ws {
49 if !prev_space {
50 out.push(' ');
51 prev_space = true;
52 }
53 } else {
54 out.push(c);
55 prev_space = false;
56 }
57 }
58 if out.ends_with(' ') {
59 out.pop();
60 }
61 Cow::Owned(out)
62 }
63 }
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn preserve_passes_through() {
73 let s = " a\tb\n c ";
74 assert_eq!(WhitespaceMode::Preserve.apply(s), s);
75 }
76
77 #[test]
78 fn replace_only_swaps_ws_chars() {
79 assert_eq!(WhitespaceMode::Replace.apply("a\tb\nc"), "a b c");
80 assert_eq!(WhitespaceMode::Replace.apply(" hello "), " hello ");
81 }
82
83 #[test]
84 fn collapse_normalizes() {
85 assert_eq!(WhitespaceMode::Collapse.apply(" a b\t\n c "), "a b c");
86 assert_eq!(WhitespaceMode::Collapse.apply("plain"), "plain");
87 assert_eq!(WhitespaceMode::Collapse.apply(""), "");
88 assert_eq!(WhitespaceMode::Collapse.apply(" "), "");
89 }
90
91 #[test]
92 fn collapse_borrows_when_clean() {
93 use std::borrow::Cow;
94 let result = WhitespaceMode::Collapse.apply("clean");
95 assert!(matches!(result, Cow::Borrowed("clean")));
96 }
97}