libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 23bdb9d2a077552937c1d2697fa82909bd5e95e4
parent ef53e579a2f6afa909038f0204d727785f1aeab3
Author: Antoine A <>
Date:   Fri, 24 Apr 2026 10:37:56 +0200

simplify xml macro

Diffstat:
Msrc/xml.rs | 86++++++++++++++++++++++++++++++++++++++++---------------------------------------
1 file changed, 44 insertions(+), 42 deletions(-)

diff --git a/src/xml.rs b/src/xml.rs @@ -31,9 +31,10 @@ macro_rules! xml { }; // Nested block ($w:ident, $name:tt $(($k:tt: $v:tt))* { $($body:tt)* }$(, $($rest:tt)*)?) => { - $w.nest($name, &[$(($k, $v)),*], |$w| { - xml!($w, $($body)*); - }); + let name = $name; + $w.open(name, &[$(($k, $v)),*]); + xml!($w, $($body)*); + $w.close(name); $(xml!($w, $($rest)*);)* }; // Empty element @@ -51,11 +52,15 @@ macro_rules! xml { #[macro_export] macro_rules! xml_build { ($name:tt $(($k:tt: $v:tt))* { $($body:tt)* }) => { - $crate::xml::XmlWriter::build(|w| { - w.nest($name, &[$(($k, $v)),*], |w| { - xml!(w, $($body)*); - }); - }) + { + let mut writer = crate::xml::XmlWriter::init(); + let w = &mut writer; + let name = $name; + w.open(name, &[$(($k, $v)),*]); + xml!(w, $($body)*); + w.close(name); + writer.finish() + } }; } @@ -63,27 +68,18 @@ pub struct XmlWriter { buff: String, } impl XmlWriter { - pub fn build<F>(f: F) -> String - where - F: FnOnce(&mut Self), - { + pub fn init() -> Self { let buff = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>"#.to_owned(); - - let mut tmp = Self { buff }; - f(&mut tmp); - tmp.buff + Self { buff } } - pub fn nest<F>(&mut self, name: &str, attrs: &[(&str, &str)], f: F) - where - F: FnOnce(&mut Self), - { + pub fn open(&mut self, name: &str, attrs: &[(&str, &str)]) { self.buff.push('<'); self.write_tag_attrs(name, attrs); self.buff.push('>'); + } - f(self); - + pub fn close(&mut self, name: &str) { self.buff.push_str("</"); self.buff.push_str(name); self.buff.push('>'); @@ -96,7 +92,9 @@ impl XmlWriter { } pub fn text<D: Display>(&mut self, name: &str, attrs: &[(&str, &str)], content: D) { - self.nest(name, attrs, |w| w.write_escaped(content)); + self.open(name, attrs); + self.write_escaped(content); + self.close(name); } fn write_tag_attrs(&mut self, name: &str, attrs: &[(&str, &str)]) { @@ -112,28 +110,32 @@ impl XmlWriter { } fn write_escaped<D: Display>(&mut self, content: D) { - struct EscapingWriter<'a>(&'a mut String); - - impl<'a> std::fmt::Write for EscapingWriter<'a> { - fn write_str(&mut self, s: &str) -> std::fmt::Result { - for c in s.chars() { - match c { - '<' => self.0.push_str("&lt;"), - '>' => self.0.push_str("&gt;"), - '&' => self.0.push_str("&amp;"), - '\'' => self.0.push_str("&apos;"), - '"' => self.0.push_str("&quot;"), - _ => self.0.push(c), - } + std::fmt::write(self, format_args!("{content}")).unwrap(); + } + + pub fn finish(self) -> String { + self.buff + } +} + +/// Write XML text content following XML escape rules +impl<'a> std::fmt::Write for XmlWriter { + fn write_str(&mut self, s: &str) -> std::fmt::Result { + if s.contains(&['<', '>', '&', '\'', '"']) { + for c in s.chars() { + match c { + '<' => self.buff.push_str("&lt;"), + '>' => self.buff.push_str("&gt;"), + '&' => self.buff.push_str("&amp;"), + '\'' => self.buff.push_str("&apos;"), + '"' => self.buff.push_str("&quot;"), + _ => self.buff.push(c), } - Ok(()) } + } else { + self.buff.push_str(s); } - std::fmt::write( - &mut EscapingWriter(&mut self.buff), - format_args!("{content}"), - ) - .unwrap(); + Ok(()) } }