pub(crate) fn write_escaped<W: std::fmt::Write>(w: &mut W, s: &str) -> std::fmt::Result {
let mut last = 0usize;
for (i, b) in s.bytes().enumerate() {
let entity = match b {
b'&' => "&",
b'<' => "<",
b'>' => ">",
b'"' => """,
b'\'' => "'",
b'\r' => " ",
_ => continue,
};
if last < i {
w.write_str(&s[last..i])?;
}
w.write_str(entity)?;
last = i + 1;
}
if last < s.len() {
w.write_str(&s[last..])?;
}
Ok(())
}
pub(crate) fn write_eur<W: std::fmt::Write>(w: &mut W, ct: i64) -> std::fmt::Result {
let sign = if ct < 0 { "-" } else { "" };
let abs = ct.unsigned_abs();
write!(w, "{sign}{}.{:02}", abs / 100, abs % 100)
}
pub(crate) struct IoWriterBridge<'a, W> {
pub(crate) inner: &'a mut W,
pub(crate) error: Option<std::io::Error>,
}
impl<W: std::io::Write> std::fmt::Write for IoWriterBridge<'_, W> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
match self.inner.write_all(s.as_bytes()) {
Ok(()) => Ok(()),
Err(e) => {
self.error = Some(e);
Err(std::fmt::Error)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn escaped_no_special_chars() {
let mut out = String::new();
write_escaped(&mut out, "Max Mustermann GmbH").unwrap();
assert_eq!(out, "Max Mustermann GmbH");
}
#[test]
fn escaped_all_entities() {
let mut out = String::new();
write_escaped(&mut out, r#"A & B < C > D " E ' F"#).unwrap();
assert_eq!(out, "A & B < C > D " E ' F");
}
#[test]
fn escaped_only_ampersand() {
let mut out = String::new();
write_escaped(&mut out, "AT&T").unwrap();
assert_eq!(out, "AT&T");
}
#[test]
fn escaped_empty() {
let mut out = String::new();
write_escaped(&mut out, "").unwrap();
assert_eq!(out, "");
}
#[test]
fn escaped_carriage_returns_preserve_text_through_xml_normalization() {
let mut out = String::new();
write_escaped(&mut out, "A\r\nB\rC\tD\nE ").unwrap();
assert_eq!(out, "A \nB C\tD\nE &#13;");
}
#[test]
fn write_eur_positive() {
let mut out = String::new();
write_eur(&mut out, 7500).unwrap();
assert_eq!(out, "75.00");
}
#[test]
fn write_eur_negative() {
let mut out = String::new();
write_eur(&mut out, -500).unwrap();
assert_eq!(out, "-5.00");
}
#[test]
fn write_eur_zero() {
let mut out = String::new();
write_eur(&mut out, 0).unwrap();
assert_eq!(out, "0.00");
}
}