edifact_primitives/
escape.rs1use crate::EdifactDelimiters;
9
10impl EdifactDelimiters {
11 fn must_escape(&self, c: char) -> bool {
15 c.is_ascii()
16 && [self.element, self.component, self.segment, self.release].contains(&(c as u8))
17 }
18
19 pub fn escape_into(&self, value: &str, out: &mut String) {
22 if !value.chars().any(|c| self.must_escape(c)) {
23 out.push_str(value);
24 return;
25 }
26 for c in value.chars() {
27 if self.must_escape(c) {
28 out.push(self.release as char);
29 }
30 out.push(c);
31 }
32 }
33
34 pub fn unescape(&self, value: &str) -> String {
38 let release = self.release as char;
39 if !value.contains(release) {
40 return value.to_string();
41 }
42 let mut out = String::with_capacity(value.len());
43 let mut chars = value.chars().peekable();
44 while let Some(c) = chars.next() {
45 if c == release {
46 if let Some(next) = chars.next() {
47 out.push(next);
48 continue;
49 }
50 }
51 out.push(c);
52 }
53 out
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 fn escaped(value: &str) -> String {
62 let mut out = String::new();
63 EdifactDelimiters::default().escape_into(value, &mut out);
64 out
65 }
66
67 #[test]
68 fn escape_keeps_multibyte_characters_whole() {
69 assert_eq!(
70 escaped("Zählpunkt? unbekannt + Müller"),
71 "Zählpunkt?? unbekannt ?+ Müller"
72 );
73 }
74
75 #[test]
76 fn escape_leaves_a_plain_value_alone() {
77 assert_eq!(escaped("Müller"), "Müller");
78 }
79
80 #[test]
81 fn escape_marks_every_delimiter() {
82 assert_eq!(escaped("a+b:c'd?e"), "a?+b?:c?'d??e");
83 }
84
85 #[test]
86 fn unescape_keeps_multibyte_characters_whole() {
87 let d = EdifactDelimiters::default();
88 assert_eq!(
89 d.unescape("Zählpunkt?? unbekannt ?+ Müller"),
90 "Zählpunkt? unbekannt + Müller"
91 );
92 assert_eq!(d.unescape("?ä"), "ä");
93 }
94
95 #[test]
96 fn unescape_inverts_escape() {
97 let d = EdifactDelimiters::default();
98 for value in ["", "x", "a+b:c'd?e", "Grüße ?+ € 'ß'", "?"] {
99 assert_eq!(d.unescape(&escaped(value)), value, "{value:?}");
100 }
101 }
102
103 #[test]
104 fn unescape_keeps_a_trailing_release_character() {
105 assert_eq!(EdifactDelimiters::default().unescape("abc?"), "abc?");
106 }
107}