edit_xml/
document.rs

1use crate::element::{Element, ElementData};
2use crate::error::{EditXMLError, Result};
3use crate::parser::{DocumentParser, ReadOptions};
4use crate::types::StandaloneValue;
5use crate::ElementBuilder;
6use quick_xml::events::{BytesCData, BytesDecl, BytesEnd, BytesPI, BytesStart, BytesText, Event};
7use quick_xml::Writer;
8use std::fs::File;
9use std::io::{Read, Write};
10use std::path::Path;
11use std::str::FromStr;
12#[cfg(feature = "document-breakdown")]
13mod breakdown;
14#[cfg(feature = "document-breakdown")]
15pub use breakdown::*;
16mod node;
17pub use node::*;
18
19/// Represents a XML document or a document fragment.
20///
21/// To build a document from scratch, use [`Document::new`].
22///
23/// To read and modify an existing document, use [parse_*](`Document#parsing`) methods.
24///
25/// To write the document, use [write_*](`Document#writing`) methods.
26///
27/// # Examples
28/// ```
29/// use edit_xml::Document;
30///
31/// let mut doc = Document::parse_str(r#"<?xml version="1.0" encoding="UTF-8"?>
32/// <package>
33///     <metadata>
34///         <author>Lewis Carol</author>
35///     </metadata>
36/// </package>
37/// "#).unwrap();
38/// let author_elem = doc
39///   .root_element()
40///   .unwrap()
41///   .find(&doc, "metadata")
42///   .unwrap()
43///   .find(&doc, "author")
44///   .unwrap();
45/// author_elem.set_text_content(&mut doc, "Lewis Carroll");
46/// let xml = doc.write_str();
47/// ```
48///
49
50#[derive(Debug)]
51pub struct Document {
52    pub(crate) counter: usize, // == self.store.len()
53    pub(crate) store: Vec<ElementData>,
54    container: Element,
55
56    pub(crate) version: String,
57    pub(crate) standalone: Option<StandaloneValue>,
58}
59impl Default for Document {
60    fn default() -> Self {
61        let (container, container_data) = Element::container();
62        Document {
63            counter: 1, // because container is id 0
64            store: vec![container_data],
65            container,
66            version: String::from("1.0"),
67            standalone: None,
68        }
69    }
70}
71impl Document {
72    /// Create a blank new xml document.
73    pub fn new() -> Document {
74        Document::default()
75    }
76
77    /// Create a new xml document with a root element.
78    ///
79    /// # Examples
80    /// ```
81    /// use edit_xml::Document;
82    /// let mut doc = Document::new_with_root("root", |root| {
83    ///    root.attribute("id", "main")
84    ///     .attribute("class", "main")
85    ///     .create_element("name", |elem| {
86    ///         elem.add_text("Cool Name")
87    ///     })
88    /// });
89    /// let root = doc.root_element().unwrap();
90    /// let name = root.find(&doc, "name").unwrap();
91    /// assert_eq!(name.text_content(&doc), "Cool Name");
92    /// ```
93    pub fn new_with_root<N, F>(root_name: N, f: F) -> Document
94    where
95        N: Into<String>,
96        F: FnOnce(ElementBuilder) -> ElementBuilder,
97    {
98        let mut doc = Document::new();
99        let root = f(ElementBuilder::new(root_name)).finish(&mut doc);
100        doc.push_root_node(root).unwrap();
101        doc
102    }
103
104    /// Get 'container' element of Document.
105    ///
106    /// The document uses an invisible 'container' element
107    /// which it uses to manage its root nodes.
108    ///
109    /// Its parent is None, and trying to change its parent will
110    /// result in an error.
111    ///
112    /// For the container element, only its `children` is relevant.
113    /// Other attributes are not used.
114    pub fn container(&self) -> Element {
115        self.container
116    }
117
118    /// Returns `true` if document doesn't have any nodes.
119    /// Returns `false` if you added a node or parsed an xml.
120    ///
121    /// You can only call `parse_*()` if document is empty.
122    pub fn is_empty(&self) -> bool {
123        self.store.len() == 1
124    }
125
126    /// Get root nodes of document.
127    pub fn root_nodes(&self) -> &Vec<Node> {
128        self.container.children(self)
129    }
130
131    /// Get first root node that is an element.
132    pub fn root_element(&self) -> Option<Element> {
133        self.container.child_elements(self).first().copied()
134    }
135
136    /// Push a node to end of root nodes.
137    /// If doc has no [`Element`], pushing a [`Node::Element`] is
138    /// equivalent to setting it as root element.
139    pub fn push_root_node(&mut self, node: impl Into<Node>) -> Result<()> {
140        let node = node.into();
141        let elem = self.container;
142        elem.push_child(self, node)
143    }
144}
145
146/// &nbsp;
147/// # Parsing
148///
149/// Below are methods for parsing xml.
150/// Parsing from string, file, and reader is supported.
151///
152/// Call `parse_*_with_opts` with custom [`ReadOptions`] to change parser behaviour.
153/// Otherwise, [`ReadOptions::default()`] is used.
154///
155impl Document {
156    pub fn parse_str(str: &str) -> Result<Document> {
157        DocumentParser::parse_reader(str.as_bytes(), ReadOptions::default())
158    }
159    pub fn parse_str_with_opts(str: &str, opts: ReadOptions) -> Result<Document> {
160        DocumentParser::parse_reader(str.as_bytes(), opts)
161    }
162
163    pub fn parse_file<P: AsRef<Path>>(path: P) -> Result<Document> {
164        let file = File::open(path)?;
165        DocumentParser::parse_reader(file, ReadOptions::default())
166    }
167    pub fn parse_file_with_opts<P: AsRef<Path>>(path: P, opts: ReadOptions) -> Result<Document> {
168        let file = File::open(path)?;
169        DocumentParser::parse_reader(file, opts)
170    }
171
172    pub fn parse_reader<R: Read>(reader: R) -> Result<Document> {
173        DocumentParser::parse_reader(reader, ReadOptions::default())
174    }
175    pub fn parse_reader_with_opts<R: Read>(reader: R, opts: ReadOptions) -> Result<Document> {
176        DocumentParser::parse_reader(reader, opts)
177    }
178}
179
180/// Options when writing XML.
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182pub struct WriteOptions {
183    /// Byte character to indent with. (default: `b' '`)
184    pub indent_char: u8,
185    /// How many indent_char should be used for indent. (default: 2)
186    pub indent_size: usize,
187    /// XML declaration should be written at the top. (default: `true`)
188    pub write_decl: bool,
189}
190impl Default for WriteOptions {
191    fn default() -> Self {
192        Self {
193            indent_char: b' ',
194            indent_size: 2,
195            write_decl: true,
196        }
197    }
198}
199
200/// &nbsp;
201/// # Writing
202///
203/// Below are methods for writing xml.
204/// The XML will be written in UTF-8.
205impl Document {
206    pub fn write_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
207        self.write_file_with_opts(path, WriteOptions::default())
208    }
209    pub fn write_file_with_opts<P: AsRef<Path>>(&self, path: P, opts: WriteOptions) -> Result<()> {
210        let mut file = File::open(path)?;
211        self.write_with_opts(&mut file, opts)
212    }
213
214    pub fn write_str(&self) -> Result<String> {
215        self.write_str_with_opts(WriteOptions::default())
216    }
217    pub fn write_str_with_opts(&self, opts: WriteOptions) -> Result<String> {
218        let mut buf: Vec<u8> = Vec::with_capacity(200);
219        self.write_with_opts(&mut buf, opts)?;
220        Ok(String::from_utf8(buf)?)
221    }
222
223    pub fn write(&self, writer: &mut impl Write) -> Result<()> {
224        self.write_with_opts(writer, WriteOptions::default())
225    }
226    pub fn write_with_opts(&self, writer: &mut impl Write, opts: WriteOptions) -> Result<()> {
227        let container = self.container();
228        let mut writer = Writer::new_with_indent(writer, opts.indent_char, opts.indent_size);
229        if opts.write_decl {
230            self.write_decl(&mut writer)?;
231        }
232        self.write_nodes(&mut writer, container.children(self))?;
233        writer.write_event(Event::Eof)?;
234        Ok(())
235    }
236
237    fn write_decl(&self, writer: &mut Writer<impl Write>) -> Result<()> {
238        let standalone = self.standalone.map(|v| v.as_str());
239        writer.write_event(Event::Decl(BytesDecl::new(
240            &self.version,
241            Some("UTF-8"),
242            standalone,
243        )))?;
244        Ok(())
245    }
246
247    fn write_nodes(&self, writer: &mut Writer<impl Write>, nodes: &[Node]) -> Result<()> {
248        for node in nodes {
249            match node {
250                Node::Element(eid) => self.write_element(writer, *eid)?,
251                Node::Text(text) => writer.write_event(Event::Text(BytesText::new(text)))?,
252                Node::DocType(text) => writer.write_event(Event::DocType(
253                    BytesText::new(&format!(" {}", text)), // add a whitespace before text
254                ))?,
255                // Comment, CData, and PI content is not escaped.
256                Node::Comment(text) => {
257                    // Unescaped Text??
258                    writer.write_event(Event::Comment(BytesText::new(text)))?
259                }
260                Node::CData(text) => {
261                    // Escaped Text ??
262                    writer.write_event(Event::CData(BytesCData::new(text)))?
263                }
264                Node::PI(text) => writer.write_event(Event::PI(BytesPI::new(text)))?,
265            };
266        }
267        Ok(())
268    }
269
270    fn write_element(&self, writer: &mut Writer<impl Write>, element: Element) -> Result<()> {
271        let name_bytes = element.full_name(self);
272        let mut start = BytesStart::new(name_bytes);
273        for (key, val) in element.attributes(self) {
274            let val = quick_xml::escape::escape(val.as_str());
275            start.push_attribute((key.as_str(), val.as_ref()));
276        }
277        for (prefix, val) in element.namespace_decls(self) {
278            let attr_name = if prefix.is_empty() {
279                "xmlns".to_string()
280            } else {
281                format!("xmlns:{}", prefix)
282            };
283
284            let val = quick_xml::escape::escape(val.as_str());
285            start.push_attribute((attr_name.as_str(), val.as_ref()));
286        }
287        if element.has_children(self) {
288            writer.write_event(Event::Start(start))?;
289            self.write_nodes(writer, element.children(self))?;
290            writer.write_event(Event::End(BytesEnd::new(name_bytes)))?;
291        } else {
292            writer.write_event(Event::Empty(start))?;
293        }
294        Ok(())
295    }
296}
297
298impl FromStr for Document {
299    type Err = EditXMLError;
300
301    fn from_str(s: &str) -> Result<Document> {
302        Document::parse_str(s)
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use super::*;
309
310    #[test]
311    fn test_add_element() {
312        let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
313        <basic>
314            Text
315            <c />
316        </basic>
317        "#;
318        let mut doc = Document::from_str(xml).unwrap();
319        let basic = doc.container().children(&doc)[0].as_element().unwrap();
320        let p = Element::new(&mut doc, "p");
321        basic.push_child(&mut doc, Node::Element(p)).unwrap();
322        assert_eq!(p.parent(&doc).unwrap(), basic);
323        assert_eq!(
324            p,
325            basic.children(&doc).last().unwrap().as_element().unwrap()
326        )
327    }
328}