use crate::EdifactDelimiters;
impl EdifactDelimiters {
fn must_escape(&self, c: char) -> bool {
c.is_ascii()
&& [self.element, self.component, self.segment, self.release].contains(&(c as u8))
}
pub fn escape_into(&self, value: &str, out: &mut String) {
if !value.chars().any(|c| self.must_escape(c)) {
out.push_str(value);
return;
}
for c in value.chars() {
if self.must_escape(c) {
out.push(self.release as char);
}
out.push(c);
}
}
pub fn unescape(&self, value: &str) -> String {
let release = self.release as char;
if !value.contains(release) {
return value.to_string();
}
let mut out = String::with_capacity(value.len());
let mut chars = value.chars().peekable();
while let Some(c) = chars.next() {
if c == release {
if let Some(next) = chars.next() {
out.push(next);
continue;
}
}
out.push(c);
}
out
}
}
#[cfg(test)]
mod tests {
use super::*;
fn escaped(value: &str) -> String {
let mut out = String::new();
EdifactDelimiters::default().escape_into(value, &mut out);
out
}
#[test]
fn escape_keeps_multibyte_characters_whole() {
assert_eq!(
escaped("Zählpunkt? unbekannt + Müller"),
"Zählpunkt?? unbekannt ?+ Müller"
);
}
#[test]
fn escape_leaves_a_plain_value_alone() {
assert_eq!(escaped("Müller"), "Müller");
}
#[test]
fn escape_marks_every_delimiter() {
assert_eq!(escaped("a+b:c'd?e"), "a?+b?:c?'d??e");
}
#[test]
fn unescape_keeps_multibyte_characters_whole() {
let d = EdifactDelimiters::default();
assert_eq!(
d.unescape("Zählpunkt?? unbekannt ?+ Müller"),
"Zählpunkt? unbekannt + Müller"
);
assert_eq!(d.unescape("?ä"), "ä");
}
#[test]
fn unescape_inverts_escape() {
let d = EdifactDelimiters::default();
for value in ["", "x", "a+b:c'd?e", "Grüße ?+ € 'ß'", "?"] {
assert_eq!(d.unescape(&escaped(value)), value, "{value:?}");
}
}
#[test]
fn unescape_keeps_a_trailing_release_character() {
assert_eq!(EdifactDelimiters::default().unescape("abc?"), "abc?");
}
}