Skip to main content

instant_xml/
ser.rs

1//! XML serialization support code
2
3use std::collections::hash_map::Entry;
4use std::collections::HashMap;
5use std::fmt::{self};
6use std::mem;
7
8use super::Error;
9use crate::ToXml;
10
11/// XML serializer for writing structured XML output
12pub struct Serializer<'xml, W: fmt::Write + ?Sized> {
13    output: &'xml mut W,
14    /// Map namespace keys to prefixes.
15    ///
16    /// The prefix map is updated using `Context` types that are held on the stack in the relevant
17    /// `ToXml` implementation. If a prefix is already defined for a given namespace, we don't
18    /// update the set with the new prefix.
19    prefixes: HashMap<&'static str, &'static str>,
20    default_ns: &'static str,
21    /// Default namespace for attributes.
22    ///
23    /// This might be different from the default namespace for child elements in case of
24    /// `force_prefix`.
25    attribute_ns: Option<&'static str>,
26    state: State,
27}
28
29impl<'xml, W: fmt::Write + ?Sized> Serializer<'xml, W> {
30    /// Create a new serializer writing to the given output
31    pub fn new(output: &'xml mut W) -> Self {
32        Self {
33            output,
34            prefixes: HashMap::new(),
35            default_ns: "",
36            attribute_ns: None,
37            state: State::Element,
38        }
39    }
40
41    /// Write the opening tag for an element
42    ///
43    /// Returns the namespace prefix if one was used.
44    ///
45    /// The `cx` parameter can be used to specify namespace declarations for the element. When
46    /// passing in `None`, you'll probably need to specify `None::<Context<0>>`.
47    pub fn write_start<'a, const N: usize>(
48        &mut self,
49        name: &'a str,
50        ns: &str,
51        cx: Option<Context<N>>,
52    ) -> Result<Element<'a, N>, Error> {
53        if self.state != State::Element {
54            return Err(Error::UnexpectedState("invalid state for element start"));
55        }
56
57        let force_prefix = cx.as_ref().is_some_and(|cx| cx.force_prefix);
58
59        let (prefix, update_default_ns) =
60            match (ns == self.default_ns, self.prefixes.get(ns), force_prefix) {
61                // Ns != default ns, force prefix: use prefix, don't change default ns
62                (false, Some(prefix), true) => {
63                    self.output.write_fmt(format_args!("<{prefix}:{name}"))?;
64                    (Some(*prefix), false)
65                }
66                // Ns != default ns, no force prefix: use prefix and set new default ns
67                (false, Some(prefix), false) => {
68                    self.output.write_fmt(format_args!("<{prefix}:{name}"))?;
69                    if let Some(cx) = &cx {
70                        self.output
71                            .write_fmt(format_args!(" xmlns=\"{}\"", cx.default_ns))?;
72                    }
73                    (Some(*prefix), true)
74                }
75                // No prefix case
76                (true, None, _) => {
77                    self.output.write_fmt(format_args!("<{name}"))?;
78                    (None, false)
79                }
80                // If ns == default ns and force-prefix is false, we ignore prefix
81                (true, Some(_), false) => {
82                    self.output.write_fmt(format_args!("<{name}"))?;
83                    // Still requalify the ns here because its not the previous default_ns
84                    if let Some(cx) = &cx {
85                        self.output
86                            .write_fmt(format_args!(" xmlns=\"{}\"", cx.default_ns))?;
87                    }
88                    (None, true)
89                }
90                // Force prefix always - when forcing prefix we dont requalify the namespace
91                (true, Some(prefix), true) => {
92                    self.output.write_fmt(format_args!("<{prefix}:{name}"))?;
93                    (Some(*prefix), false)
94                }
95                _ => {
96                    self.output
97                        .write_fmt(format_args!("<{name} xmlns=\"{ns}\""))?;
98                    (None, true)
99                }
100            };
101
102        self.state = State::Attribute;
103        self.attribute_ns = cx.as_ref().map(|cx| cx.default_ns);
104        let Some(cx) = cx else {
105            return Ok(Element {
106                prefix,
107                name,
108                parent: None,
109            });
110        };
111
112        let mut old = Context::default();
113        let prev = match update_default_ns {
114            true => mem::replace(&mut self.default_ns, cx.default_ns),
115            false => self.default_ns,
116        };
117        let _ = mem::replace(&mut old.default_ns, prev);
118
119        let mut used = 0;
120        for prefix in cx.prefixes.into_iter() {
121            if prefix.prefix.is_empty() {
122                continue;
123            }
124
125            if self.prefixes.contains_key(prefix.ns) {
126                continue;
127            }
128
129            self.output
130                .write_fmt(format_args!(" xmlns:{}=\"{}\"", prefix.prefix, prefix.ns))?;
131
132            let prev = match self.prefixes.entry(prefix.ns) {
133                Entry::Occupied(mut entry) => mem::replace(entry.get_mut(), prefix.prefix),
134                Entry::Vacant(entry) => {
135                    entry.insert(prefix.prefix);
136                    ""
137                }
138            };
139
140            old.prefixes[used] = Prefix {
141                ns: prefix.ns,
142                prefix: prev,
143            };
144            used += 1;
145        }
146
147        Ok(Element {
148            prefix,
149            name,
150            parent: Some(old),
151        })
152    }
153
154    /// Write an attribute with the given name and value
155    pub fn write_attr<V: ToXml + ?Sized>(
156        &mut self,
157        name: &str,
158        ns: &str,
159        value: &V,
160    ) -> Result<(), Error> {
161        if self.state != State::Attribute {
162            return Err(Error::UnexpectedState("invalid state for attribute"));
163        }
164
165        let attr_ns = self.attribute_ns.unwrap_or(self.default_ns);
166        match ns.is_empty() || ns == attr_ns {
167            true => self.output.write_fmt(format_args!(" {name}=\""))?,
168            false => {
169                let prefix = self
170                    .prefixes
171                    .get(ns)
172                    .ok_or(Error::UnexpectedState("unknown prefix"))?;
173                self.output.write_fmt(format_args!(" {prefix}:{name}=\""))?;
174            }
175        }
176
177        self.state = State::Scalar;
178        value.serialize(None, self)?;
179        self.state = State::Attribute;
180        self.output.write_char('"')?;
181        Ok(())
182    }
183
184    /// Write a string value (text content or attribute value)
185    pub fn write_str<V: fmt::Display + ?Sized>(&mut self, value: &V) -> Result<(), Error> {
186        if !matches!(self.state, State::Element | State::Scalar) {
187            return Err(Error::UnexpectedState("invalid state for scalar"));
188        }
189
190        self.output.write_fmt(format_args!("{value}"))?;
191        self.state = State::Element;
192        Ok(())
193    }
194
195    /// Complete the opening tag and transition to element content
196    pub fn end_start(&mut self) -> Result<(), Error> {
197        if self.state != State::Attribute {
198            return Err(Error::UnexpectedState("invalid state for element end"));
199        }
200
201        self.output.write_char('>')?;
202        self.attribute_ns = None;
203        self.state = State::Element;
204        Ok(())
205    }
206
207    /// Close an empty element (self-closing tag)
208    pub fn end_empty(&mut self) -> Result<(), Error> {
209        if self.state != State::Attribute {
210            return Err(Error::UnexpectedState("invalid state for element end"));
211        }
212
213        self.output.write_str(" />")?;
214        self.attribute_ns = None;
215        self.state = State::Element;
216        Ok(())
217    }
218
219    /// Write the closing tag for an element
220    pub fn write_close<const N: usize>(&mut self, element: Element<'_, N>) -> Result<(), Error> {
221        if self.state != State::Element {
222            return Err(Error::UnexpectedState("invalid state for close element"));
223        }
224
225        match element.prefix {
226            Some(prefix) => self
227                .output
228                .write_fmt(format_args!("</{prefix}:{}>", element.name))?,
229            None => self.output.write_fmt(format_args!("</{}>", element.name))?,
230        }
231
232        let Some(old) = element.parent else {
233            return Ok(());
234        };
235
236        let _ = mem::replace(&mut self.default_ns, old.default_ns);
237        for prefix in old.prefixes.into_iter() {
238            if prefix.ns.is_empty() && prefix.prefix.is_empty() {
239                continue;
240            }
241
242            let mut entry = match self.prefixes.entry(prefix.ns) {
243                Entry::Occupied(entry) => entry,
244                Entry::Vacant(_) => unreachable!(),
245            };
246
247            match prefix.prefix {
248                "" => {
249                    entry.remove();
250                }
251                prev => {
252                    let _ = mem::replace(entry.get_mut(), prev);
253                }
254            }
255        }
256
257        Ok(())
258    }
259
260    /// Get the prefix for a namespace URI, if any
261    pub fn prefix(&self, ns: &str) -> Option<&'static str> {
262        self.prefixes.get(ns).copied()
263    }
264
265    /// Get the current default namespace URI
266    pub fn default_ns(&self) -> &'static str {
267        self.default_ns
268    }
269}
270
271/// An element being serialized, used for tracking namespace context
272#[non_exhaustive]
273pub struct Element<'a, const N: usize> {
274    /// Prefix of the element, if any
275    pub prefix: Option<&'static str>,
276    /// Local name of the element
277    pub name: &'a str,
278    /// Namespace context of the parent element, if any
279    pub parent: Option<Context<N>>,
280}
281
282/// Namespace context for serialization
283#[derive(Debug)]
284#[non_exhaustive]
285pub struct Context<const N: usize> {
286    /// The default namespace URI
287    pub default_ns: &'static str,
288    /// Array of namespace prefix mappings
289    pub prefixes: [Prefix; N],
290    /// Force prefix to be included always
291    pub force_prefix: bool,
292}
293
294impl<const N: usize> Default for Context<N> {
295    fn default() -> Self {
296        Self {
297            default_ns: Default::default(),
298            prefixes: [Prefix { prefix: "", ns: "" }; N],
299            force_prefix: false,
300        }
301    }
302}
303
304/// A namespace prefix mapping
305#[derive(Clone, Copy, Debug, Default)]
306pub struct Prefix {
307    /// The namespace prefix
308    pub prefix: &'static str,
309    /// The namespace URI
310    pub ns: &'static str,
311}
312
313#[derive(Debug, Eq, PartialEq)]
314enum State {
315    Attribute,
316    Element,
317    Scalar,
318}