xml5ever/serialize/
mod.rs

1// Copyright 2014-2017 The html5ever Project Developers. See the
2// COPYRIGHT file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use crate::tree_builder::NamespaceMap;
11use crate::QualName;
12pub use markup5ever::serialize::{AttrRef, Serialize, Serializer, TraversalScope};
13use std::io::{self, Write};
14
15#[derive(Clone)]
16/// Struct for setting serializer options.
17pub struct SerializeOpts {
18    /// Serialize the root node? Default: ChildrenOnly
19    pub traversal_scope: TraversalScope,
20}
21
22impl Default for SerializeOpts {
23    fn default() -> SerializeOpts {
24        SerializeOpts {
25            traversal_scope: TraversalScope::ChildrenOnly(None),
26        }
27    }
28}
29
30/// Method for serializing generic node to a given writer.
31pub fn serialize<Wr, T>(writer: Wr, node: &T, opts: SerializeOpts) -> io::Result<()>
32where
33    Wr: Write,
34    T: Serialize,
35{
36    let mut ser = XmlSerializer::new(writer);
37    node.serialize(&mut ser, opts.traversal_scope)
38}
39
40/// Struct used for serializing nodes into a text that other XML
41/// parses can read.
42///
43/// Serializer contains a set of functions (start_elem, end_elem...)
44/// that make parsing nodes easier.
45pub struct XmlSerializer<Wr> {
46    writer: Wr,
47    namespace_stack: NamespaceMapStack,
48}
49
50#[derive(Debug)]
51struct NamespaceMapStack(Vec<NamespaceMap>);
52
53impl NamespaceMapStack {
54    fn new() -> NamespaceMapStack {
55        NamespaceMapStack(vec![])
56    }
57
58    fn push(&mut self, namespace: NamespaceMap) {
59        self.0.push(namespace);
60    }
61
62    fn pop(&mut self) {
63        self.0.pop();
64    }
65}
66
67/// Writes given text into the Serializer, escaping it,
68/// depending on where the text is written inside the tag or attribute value.
69///
70/// For example
71///```text
72///    <tag>'&-quotes'</tag>   becomes      <tag>'&amp;-quotes'</tag>
73///    <tag = "'&-quotes'">    becomes      <tag = "&apos;&amp;-quotes&apos;"
74///```
75fn write_to_buf_escaped<W: Write>(writer: &mut W, text: &str, attr_mode: bool) -> io::Result<()> {
76    for c in text.chars() {
77        match c {
78            '&' => writer.write_all(b"&amp;"),
79            '\'' if attr_mode => writer.write_all(b"&apos;"),
80            '"' if attr_mode => writer.write_all(b"&quot;"),
81            '<' if !attr_mode => writer.write_all(b"&lt;"),
82            '>' if !attr_mode => writer.write_all(b"&gt;"),
83            c => writer.write_fmt(format_args!("{c}")),
84        }?;
85    }
86    Ok(())
87}
88
89#[inline]
90fn write_qual_name<W: Write>(writer: &mut W, name: &QualName) -> io::Result<()> {
91    if let Some(ref prefix) = name.prefix {
92        writer.write_all(prefix.as_bytes())?;
93        writer.write_all(b":")?;
94    }
95
96    writer.write_all(name.local.as_bytes())?;
97    Ok(())
98}
99
100impl<Wr: Write> XmlSerializer<Wr> {
101    /// Creates a new Serializier from a writer and given serialization options.
102    pub fn new(writer: Wr) -> Self {
103        XmlSerializer {
104            writer,
105            namespace_stack: NamespaceMapStack::new(),
106        }
107    }
108
109    #[inline(always)]
110    fn qual_name(&mut self, name: &QualName) -> io::Result<()> {
111        self.find_or_insert_ns(name);
112        write_qual_name(&mut self.writer, name)
113    }
114
115    #[inline(always)]
116    fn qual_attr_name(&mut self, name: &QualName) -> io::Result<()> {
117        self.find_or_insert_ns(name);
118        write_qual_name(&mut self.writer, name)
119    }
120
121    fn find_uri(&self, name: &QualName) -> bool {
122        let mut found = false;
123        for stack in self.namespace_stack.0.iter().rev() {
124            if let Some(Some(el)) = stack.get(&name.prefix) {
125                found = *el == name.ns;
126                break;
127            }
128        }
129        found
130    }
131
132    fn find_or_insert_ns(&mut self, name: &QualName) {
133        if (name.prefix.is_some() || !name.ns.is_empty()) && !self.find_uri(name) {
134            if let Some(last_ns) = self.namespace_stack.0.last_mut() {
135                last_ns.insert(name);
136            }
137        }
138    }
139}
140
141impl<Wr: Write> Serializer for XmlSerializer<Wr> {
142    /// Serializes given start element into text. Start element contains
143    /// qualified name and an attributes iterator.
144    fn start_elem<'a, AttrIter>(&mut self, name: QualName, attrs: AttrIter) -> io::Result<()>
145    where
146        AttrIter: Iterator<Item = AttrRef<'a>>,
147    {
148        self.namespace_stack.push(NamespaceMap::empty());
149
150        self.writer.write_all(b"<")?;
151        self.qual_name(&name)?;
152        if let Some(current_namespace) = self.namespace_stack.0.last() {
153            for (prefix, url_opt) in current_namespace.get_scope_iter() {
154                self.writer.write_all(b" xmlns")?;
155                if let Some(ref p) = *prefix {
156                    self.writer.write_all(b":")?;
157                    self.writer.write_all(p.as_bytes())?;
158                }
159
160                self.writer.write_all(b"=\"")?;
161                let url = if let Some(ref a) = *url_opt {
162                    a.as_bytes()
163                } else {
164                    b""
165                };
166                self.writer.write_all(url)?;
167                self.writer.write_all(b"\"")?;
168            }
169        }
170        for (name, value) in attrs {
171            self.writer.write_all(b" ")?;
172            self.qual_attr_name(name)?;
173            self.writer.write_all(b"=\"")?;
174            write_to_buf_escaped(&mut self.writer, value, true)?;
175            self.writer.write_all(b"\"")?;
176        }
177        self.writer.write_all(b">")?;
178        Ok(())
179    }
180
181    /// Serializes given end element into text.
182    fn end_elem(&mut self, name: QualName) -> io::Result<()> {
183        self.namespace_stack.pop();
184        self.writer.write_all(b"</")?;
185        self.qual_name(&name)?;
186        self.writer.write_all(b">")
187    }
188
189    /// Serializes comment into text.
190    fn write_comment(&mut self, text: &str) -> io::Result<()> {
191        self.writer.write_all(b"<!--")?;
192        self.writer.write_all(text.as_bytes())?;
193        self.writer.write_all(b"-->")
194    }
195
196    /// Serializes given doctype
197    fn write_doctype(&mut self, name: &str) -> io::Result<()> {
198        self.writer.write_all(b"<!DOCTYPE ")?;
199        self.writer.write_all(name.as_bytes())?;
200        self.writer.write_all(b">")
201    }
202
203    /// Serializes text for a node or an attributes.
204    fn write_text(&mut self, text: &str) -> io::Result<()> {
205        write_to_buf_escaped(&mut self.writer, text, false)
206    }
207
208    /// Serializes given processing instruction.
209    fn write_processing_instruction(&mut self, target: &str, data: &str) -> io::Result<()> {
210        self.writer.write_all(b"<?")?;
211        self.writer.write_all(target.as_bytes())?;
212        self.writer.write_all(b" ")?;
213        self.writer.write_all(data.as_bytes())?;
214        self.writer.write_all(b"?>")
215    }
216}