Skip to main content

tinyxml2/
document.rs

1//! XML document container and DOM tree manipulation.
2
3use alloc::format;
4use alloc::string::{String, ToString};
5use alloc::vec::Vec;
6use core::{fmt, str};
7
8use crate::ParseOptions;
9use crate::arena::{Arena, NodeId};
10use crate::error::{Result, XmlError};
11use crate::node::{Attribute, ElementData, NodeData, NodeKind, TextData};
12use crate::parser::Parser;
13
14/// The main XML document container.
15///
16/// Owns the node arena, root node, and tracks the current parse state/error.
17#[derive(Debug)]
18pub struct Document {
19    pub(crate) arena: Arena<NodeData>,
20    root: NodeId,
21    error: Option<XmlError>,
22    options: ParseOptions,
23    has_bom: bool,
24}
25
26impl Document {
27    /// Creates a new, empty XML document containing only a root Document node.
28    #[must_use]
29    pub fn new() -> Self {
30        let mut arena = Arena::new();
31        let root = arena.alloc(NodeData::new(NodeKind::Document, 1));
32        Self {
33            arena,
34            root,
35            error: None,
36            options: ParseOptions::default(),
37            has_bom: false,
38        }
39    }
40
41    /// Creates a new, empty XML document with custom parse options.
42    #[must_use]
43    pub fn with_options(options: ParseOptions) -> Self {
44        let mut arena = Arena::new();
45        let root = arena.alloc(NodeData::new(NodeKind::Document, 1));
46        Self {
47            arena,
48            root,
49            error: None,
50            options,
51            has_bom: false,
52        }
53    }
54
55    /// Returns the root `NodeId` of the document.
56    #[must_use]
57    pub const fn root(&self) -> NodeId {
58        self.root
59    }
60
61    /// Returns the kind of the specified node, if it exists.
62    #[must_use]
63    pub fn node_kind(&self, node: NodeId) -> Option<&NodeKind> {
64        self.arena.get(node).map(|d| &d.kind)
65    }
66
67    /// Returns the 1-based source line number where this node was parsed.
68    #[must_use]
69    pub fn line_num(&self, node: NodeId) -> Option<u32> {
70        self.arena.get(node).map(|d| d.line_num)
71    }
72
73    /// Returns the current error state of the document, if any.
74    #[must_use]
75    pub fn error(&self) -> Option<XmlError> {
76        self.error.clone()
77    }
78
79    /// Returns the line number of the last error, if available.
80    #[must_use]
81    pub fn error_line(&self) -> Option<u32> {
82        self.error.as_ref().and_then(XmlError::line)
83    }
84
85    /// Sets the error state of the document.
86    pub(crate) fn set_error(&mut self, err: XmlError) {
87        self.error = Some(err);
88    }
89
90    /// Resets the document to an empty state, invalidating all existing `NodeId`s.
91    pub fn clear(&mut self) {
92        self.arena.clear();
93        self.error = None;
94        self.has_bom = false;
95        self.root = self.arena.alloc(NodeData::new(NodeKind::Document, 1));
96    }
97
98    /// Returns whether a Byte Order Mark (BOM) was detected during parsing.
99    #[must_use]
100    pub const fn has_bom(&self) -> bool {
101        self.has_bom
102    }
103
104    /// Sets whether to output a Byte Order Mark (BOM) when serializing.
105    pub fn set_bom(&mut self, use_bom: bool) {
106        self.has_bom = use_bom;
107    }
108
109    /// Returns a reference to the document's parse options.
110    #[must_use]
111    pub const fn options(&self) -> &ParseOptions {
112        &self.options
113    }
114
115    /// Returns a mutable reference to the document's parse options.
116    pub fn options_mut(&mut self) -> &mut ParseOptions {
117        &mut self.options
118    }
119
120    // --- Parsing Entry Points ---
121
122    /// Parses an XML document from a string slice in place.
123    ///
124    /// Any existing DOM structure is cleared.
125    pub fn parse_str(&mut self, xml: &str) -> Result<()> {
126        self.clear();
127
128        // Truncate at first null byte to match C++ null-terminated behavior
129        let truncated_xml = xml.split('\0').next().unwrap_or("");
130
131        // Detect and skip BOM
132        let (xml_after_bom, had_bom) = crate::util::strip_bom(truncated_xml);
133        self.has_bom = had_bom;
134
135        let mut parser = Parser::new(xml_after_bom, self.options.clone());
136        match parser.parse_document(self) {
137            Ok(()) => Ok(()),
138            Err(e) => {
139                self.set_error(e.clone());
140                Err(e)
141            }
142        }
143    }
144
145    /// Parses an XML document from a byte slice in place.
146    ///
147    /// Rejects non-UTF-8 inputs. Any existing DOM structure is cleared.
148    pub fn parse_bytes_mut(&mut self, bytes: &[u8]) -> Result<()> {
149        let s = str::from_utf8(bytes).map_err(|e| {
150            let err = XmlError::Parse {
151                kind: crate::error::ParseErrorKind::General,
152                line: 1,
153                message: Some(format!("Invalid UTF-8 sequence: {e}")),
154            };
155            self.set_error(err.clone());
156            err
157        })?;
158        self.parse_str(s)
159    }
160
161    /// Loads and parses an XML file from the given path in place.
162    ///
163    /// Any existing DOM structure is cleared.
164    #[cfg(feature = "std")]
165    pub fn load_file_mut(&mut self, path: impl AsRef<std::path::Path>) -> Result<()> {
166        let bytes = std::fs::read(path)?;
167        self.parse_bytes_mut(&bytes)
168    }
169
170    /// Parses an XML document from a string slice, returning the new Document.
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use tinyxml2::Document;
176    ///
177    /// let doc = Document::parse(r#"<book isbn="9780441172719"><title>Dune</title></book>"#)?;
178    /// let book = doc.root_element().expect("book element");
179    ///
180    /// assert_eq!(doc.attribute(book, "isbn"), Some("9780441172719"));
181    /// # Ok::<(), tinyxml2::XmlError>(())
182    /// ```
183    pub fn parse(xml: &str) -> Result<Self> {
184        let mut doc = Self::new();
185        doc.parse_str(xml)?;
186        Ok(doc)
187    }
188
189    /// Parses an XML document from a byte slice, returning the new Document.
190    ///
191    /// Rejects non-UTF-8 inputs.
192    pub fn parse_bytes(bytes: &[u8]) -> Result<Self> {
193        let mut doc = Self::new();
194        doc.parse_bytes_mut(bytes)?;
195        Ok(doc)
196    }
197
198    /// Loads and parses an XML file from the given path, returning the new Document.
199    #[cfg(feature = "std")]
200    pub fn load_file(path: impl AsRef<std::path::Path>) -> Result<Self> {
201        let mut doc = Self::new();
202        doc.load_file_mut(path)?;
203        Ok(doc)
204    }
205
206    // --- Factory Methods ---
207
208    /// Creates a new detached Element node in the document's arena.
209    ///
210    /// # Examples
211    ///
212    /// ```
213    /// use tinyxml2::Document;
214    ///
215    /// let mut doc = Document::new();
216    /// let library = doc.new_element("library");
217    /// doc.set_attribute(library, "name", "Central")?;
218    /// doc.insert_end_child(doc.root(), library)?;
219    ///
220    /// assert_eq!(doc.root_element(), Some(library));
221    /// assert_eq!(doc.attribute(library, "name"), Some("Central"));
222    /// # Ok::<(), tinyxml2::XmlError>(())
223    /// ```
224    pub fn new_element(&mut self, name: &str) -> NodeId {
225        let kind = NodeKind::Element(ElementData {
226            name: name.to_string(),
227            attributes: Vec::new(),
228        });
229        self.arena.alloc(NodeData::new(kind, 1))
230    }
231
232    /// Creates a new detached Text node in the document's arena.
233    pub fn new_text(&mut self, text: &str) -> NodeId {
234        let kind = NodeKind::Text(TextData {
235            content: text.to_string(),
236            is_cdata: false,
237        });
238        self.arena.alloc(NodeData::new(kind, 1))
239    }
240
241    /// Creates a new detached CDATA Text node in the document's arena.
242    pub fn new_cdata(&mut self, text: &str) -> NodeId {
243        let kind = NodeKind::Text(TextData {
244            content: text.to_string(),
245            is_cdata: true,
246        });
247        self.arena.alloc(NodeData::new(kind, 1))
248    }
249
250    /// Creates a new detached Comment node in the document's arena.
251    pub fn new_comment(&mut self, text: &str) -> NodeId {
252        let kind = NodeKind::Comment(text.to_string());
253        self.arena.alloc(NodeData::new(kind, 1))
254    }
255
256    /// Creates a new detached Declaration node in the document's arena.
257    pub fn new_declaration(&mut self, decl: &str) -> NodeId {
258        let kind = NodeKind::Declaration(ElementData {
259            name: decl.to_string(),
260            attributes: Vec::new(),
261        });
262        self.arena.alloc(NodeData::new(kind, 1))
263    }
264
265    /// Creates a new detached Unknown node in the document's arena.
266    pub fn new_unknown(&mut self, text: &str) -> NodeId {
267        let kind = NodeKind::Unknown(text.to_string());
268        self.arena.alloc(NodeData::new(kind, 1))
269    }
270
271    // --- Navigation APIs ---
272
273    /// Returns the parent of the specified node, if it exists.
274    #[must_use]
275    pub fn parent(&self, node: NodeId) -> Option<NodeId> {
276        self.arena.get(node).and_then(|d| d.parent)
277    }
278
279    /// Returns the first child of the specified node, if it exists.
280    #[must_use]
281    pub fn first_child(&self, node: NodeId) -> Option<NodeId> {
282        self.arena.get(node).and_then(|d| d.first_child)
283    }
284
285    /// Returns the last child of the specified node, if it exists.
286    #[must_use]
287    pub fn last_child(&self, node: NodeId) -> Option<NodeId> {
288        self.arena.get(node).and_then(|d| d.last_child)
289    }
290
291    /// Returns the previous sibling of the specified node, if it exists.
292    #[must_use]
293    pub fn prev_sibling(&self, node: NodeId) -> Option<NodeId> {
294        self.arena.get(node).and_then(|d| d.prev_sibling)
295    }
296
297    /// Returns the next sibling of the specified node, if it exists.
298    #[must_use]
299    pub fn next_sibling(&self, node: NodeId) -> Option<NodeId> {
300        self.arena.get(node).and_then(|d| d.next_sibling)
301    }
302
303    /// Returns the first child Element of the specified node, optionally matching a tag name.
304    ///
305    /// # Examples
306    ///
307    /// ```
308    /// use tinyxml2::Document;
309    ///
310    /// let doc = Document::parse("<feed><title>News</title><entry id=\"1\"/></feed>")?;
311    /// let feed = doc.root_element().expect("feed element");
312    /// let entry = doc.first_child_element(feed, Some("entry")).expect("entry element");
313    ///
314    /// assert_eq!(doc.attribute(entry, "id"), Some("1"));
315    /// # Ok::<(), tinyxml2::XmlError>(())
316    /// ```
317    #[must_use]
318    pub fn first_child_element(&self, node: NodeId, name: Option<&str>) -> Option<NodeId> {
319        let mut current = self.first_child(node);
320        while let Some(curr) = current {
321            if let Some(data) = self.arena.get(curr) {
322                if let NodeKind::Element(el_data) = &data.kind {
323                    if name.is_none_or(|n| el_data.name == n) {
324                        return Some(curr);
325                    }
326                }
327            }
328            current = self.next_sibling(curr);
329        }
330        None
331    }
332
333    /// Returns the last child Element of the specified node, optionally matching a tag name.
334    #[must_use]
335    pub fn last_child_element(&self, node: NodeId, name: Option<&str>) -> Option<NodeId> {
336        let mut current = self.last_child(node);
337        while let Some(curr) = current {
338            if let Some(data) = self.arena.get(curr) {
339                if let NodeKind::Element(el_data) = &data.kind {
340                    if name.is_none_or(|n| el_data.name == n) {
341                        return Some(curr);
342                    }
343                }
344            }
345            current = self.prev_sibling(curr);
346        }
347        None
348    }
349
350    /// Returns the next sibling Element of the specified node, optionally matching a tag name.
351    ///
352    /// # Examples
353    ///
354    /// ```
355    /// use tinyxml2::Document;
356    ///
357    /// let doc = Document::parse("<playlist><track/><note>skip me</note><track id=\"next\"/></playlist>")?;
358    /// let playlist = doc.root_element().expect("playlist element");
359    /// let first_track = doc.first_child_element(playlist, Some("track")).expect("first track");
360    /// let next_track = doc.next_sibling_element(first_track, Some("track")).expect("next track");
361    ///
362    /// assert_eq!(doc.attribute(next_track, "id"), Some("next"));
363    /// # Ok::<(), tinyxml2::XmlError>(())
364    /// ```
365    #[must_use]
366    pub fn next_sibling_element(&self, node: NodeId, name: Option<&str>) -> Option<NodeId> {
367        let mut current = self.next_sibling(node);
368        while let Some(curr) = current {
369            if let Some(data) = self.arena.get(curr) {
370                if let NodeKind::Element(el_data) = &data.kind {
371                    if name.is_none_or(|n| el_data.name == n) {
372                        return Some(curr);
373                    }
374                }
375            }
376            current = self.next_sibling(curr);
377        }
378        None
379    }
380
381    /// Returns the previous sibling Element of the specified node, optionally matching a tag name.
382    #[must_use]
383    pub fn prev_sibling_element(&self, node: NodeId, name: Option<&str>) -> Option<NodeId> {
384        let mut current = self.prev_sibling(node);
385        while let Some(curr) = current {
386            if let Some(data) = self.arena.get(curr) {
387                if let NodeKind::Element(el_data) = &data.kind {
388                    if name.is_none_or(|n| el_data.name == n) {
389                        return Some(curr);
390                    }
391                }
392            }
393            current = self.prev_sibling(curr);
394        }
395        None
396    }
397
398    /// Returns the root Element of the document (the first element child of the root document node).
399    ///
400    /// # Examples
401    ///
402    /// ```
403    /// use tinyxml2::Document;
404    ///
405    /// let doc = Document::parse("<config><theme>dark</theme></config>")?;
406    /// let root = doc.root_element().expect("root element");
407    ///
408    /// assert_eq!(doc.element_ref(root).expect("element").name(), "config");
409    /// # Ok::<(), tinyxml2::XmlError>(())
410    /// ```
411    #[must_use]
412    pub fn root_element(&self) -> Option<NodeId> {
413        self.first_child_element(self.root, None)
414    }
415
416    // --- Invariant Checks & Tree Linkage Helpers ---
417
418    /// Returns `true` if `ancestor` is an ancestor of `descendant` (or the same node).
419    fn is_ancestor(&self, ancestor: NodeId, mut descendant: NodeId) -> bool {
420        if ancestor == descendant {
421            return true;
422        }
423        while let Some(parent) = self.parent(descendant) {
424            if parent == ancestor {
425                return true;
426            }
427            descendant = parent;
428        }
429        false
430    }
431
432    /// Unlinks a node from its parent and siblings.
433    fn unlink(&mut self, node: NodeId) -> Result<()> {
434        let data = self.arena.get(node).ok_or(XmlError::InvalidNodeId)?.clone();
435        if let Some(parent) = data.parent {
436            let p_data = self.arena.get_mut(parent).ok_or(XmlError::InvalidNodeId)?;
437            if p_data.first_child == Some(node) {
438                p_data.first_child = data.next_sibling;
439            }
440            if p_data.last_child == Some(node) {
441                p_data.last_child = data.prev_sibling;
442            }
443        }
444        if let Some(prev) = data.prev_sibling {
445            if let Some(prev_node) = self.arena.get_mut(prev) {
446                prev_node.next_sibling = data.next_sibling;
447            }
448        }
449        if let Some(next) = data.next_sibling {
450            if let Some(next_node) = self.arena.get_mut(next) {
451                next_node.prev_sibling = data.prev_sibling;
452            }
453        }
454
455        let node_mut = self.arena.get_mut(node).ok_or(XmlError::InvalidNodeId)?;
456        node_mut.parent = None;
457        node_mut.prev_sibling = None;
458        node_mut.next_sibling = None;
459        Ok(())
460    }
461
462    // --- Tree Mutation APIs ---
463
464    /// Inserts `child` as the last child of `parent`.
465    pub fn insert_end_child(&mut self, parent: NodeId, child: NodeId) -> Result<NodeId> {
466        if !self.arena.contains(parent) || !self.arena.contains(child) {
467            return Err(XmlError::InvalidNodeId);
468        }
469        if self.is_ancestor(child, parent) {
470            return Err(XmlError::InvalidNodeId);
471        }
472
473        self.unlink(child)?;
474
475        let parent_data = self.arena.get(parent).ok_or(XmlError::InvalidNodeId)?;
476        let old_last = parent_data.last_child;
477
478        if let Some(last) = old_last {
479            let last_node = self.arena.get_mut(last).ok_or(XmlError::InvalidNodeId)?;
480            last_node.next_sibling = Some(child);
481        }
482
483        let child_node = self.arena.get_mut(child).ok_or(XmlError::InvalidNodeId)?;
484        child_node.parent = Some(parent);
485        child_node.prev_sibling = old_last;
486        child_node.next_sibling = None;
487
488        let parent_node = self.arena.get_mut(parent).ok_or(XmlError::InvalidNodeId)?;
489        if parent_node.first_child.is_none() {
490            parent_node.first_child = Some(child);
491        }
492        parent_node.last_child = Some(child);
493
494        Ok(child)
495    }
496
497    /// Inserts `child` as the first child of `parent`.
498    pub fn insert_first_child(&mut self, parent: NodeId, child: NodeId) -> Result<NodeId> {
499        if !self.arena.contains(parent) || !self.arena.contains(child) {
500            return Err(XmlError::InvalidNodeId);
501        }
502        if self.is_ancestor(child, parent) {
503            return Err(XmlError::InvalidNodeId);
504        }
505
506        self.unlink(child)?;
507
508        let parent_data = self.arena.get(parent).ok_or(XmlError::InvalidNodeId)?;
509        let old_first = parent_data.first_child;
510
511        if let Some(first) = old_first {
512            let first_node = self.arena.get_mut(first).ok_or(XmlError::InvalidNodeId)?;
513            first_node.prev_sibling = Some(child);
514        }
515
516        let child_node = self.arena.get_mut(child).ok_or(XmlError::InvalidNodeId)?;
517        child_node.parent = Some(parent);
518        child_node.prev_sibling = None;
519        child_node.next_sibling = old_first;
520
521        let parent_node = self.arena.get_mut(parent).ok_or(XmlError::InvalidNodeId)?;
522        if parent_node.last_child.is_none() {
523            parent_node.last_child = Some(child);
524        }
525        parent_node.first_child = Some(child);
526
527        Ok(child)
528    }
529
530    /// Inserts `child` immediately after `after`.
531    pub fn insert_after_child(&mut self, after: NodeId, child: NodeId) -> Result<NodeId> {
532        if !self.arena.contains(after) || !self.arena.contains(child) {
533            return Err(XmlError::InvalidNodeId);
534        }
535        let parent = self.parent(after).ok_or(XmlError::InvalidNodeId)?;
536        if self.is_ancestor(child, parent) {
537            return Err(XmlError::InvalidNodeId);
538        }
539
540        self.unlink(child)?;
541
542        let after_data = self.arena.get(after).ok_or(XmlError::InvalidNodeId)?;
543        let old_next = after_data.next_sibling;
544
545        if let Some(next) = old_next {
546            let next_node = self.arena.get_mut(next).ok_or(XmlError::InvalidNodeId)?;
547            next_node.prev_sibling = Some(child);
548        }
549
550        let child_node = self.arena.get_mut(child).ok_or(XmlError::InvalidNodeId)?;
551        child_node.parent = Some(parent);
552        child_node.prev_sibling = Some(after);
553        child_node.next_sibling = old_next;
554
555        let after_node = self.arena.get_mut(after).ok_or(XmlError::InvalidNodeId)?;
556        after_node.next_sibling = Some(child);
557
558        let parent_node = self.arena.get_mut(parent).ok_or(XmlError::InvalidNodeId)?;
559        if parent_node.last_child == Some(after) {
560            parent_node.last_child = Some(child);
561        }
562
563        Ok(child)
564    }
565
566    /// Helper for recursive deallocation of a node and all of its descendants.
567    fn delete_recursive(&mut self, node: NodeId) {
568        let mut next_child = self.first_child(node);
569        while let Some(child) = next_child {
570            let sibling = self.next_sibling(child);
571            self.delete_recursive(child);
572            next_child = sibling;
573        }
574        self.arena.dealloc(node);
575    }
576
577    /// Removes `child` from `parent` and deallocates it recursively.
578    pub fn delete_child(&mut self, parent: NodeId, child: NodeId) -> Result<()> {
579        if !self.arena.contains(parent) || !self.arena.contains(child) {
580            return Err(XmlError::InvalidNodeId);
581        }
582        if self.parent(child) != Some(parent) {
583            return Err(XmlError::InvalidNodeId);
584        }
585
586        self.unlink(child)?;
587        self.delete_recursive(child);
588        Ok(())
589    }
590
591    /// Removes and deallocates all children of `parent`.
592    pub fn delete_children(&mut self, parent: NodeId) -> Result<()> {
593        if !self.arena.contains(parent) {
594            return Err(XmlError::InvalidNodeId);
595        }
596
597        let mut next_child = self.first_child(parent);
598        while let Some(child) = next_child {
599            let sibling = self.next_sibling(child);
600            self.unlink(child)?;
601            self.delete_recursive(child);
602            next_child = sibling;
603        }
604
605        let parent_node = self.arena.get_mut(parent).ok_or(XmlError::InvalidNodeId)?;
606        parent_node.first_child = None;
607        parent_node.last_child = None;
608        Ok(())
609    }
610
611    /// Unlinks `node` from its parent and recursively deallocates it.
612    pub fn delete_node(&mut self, node: NodeId) -> Result<()> {
613        if !self.arena.contains(node) {
614            return Err(XmlError::InvalidNodeId);
615        }
616        if node == self.root {
617            return Err(XmlError::InvalidNodeId);
618        }
619
620        self.unlink(node)?;
621        self.delete_recursive(node);
622        Ok(())
623    }
624
625    // --- Clone Operations ---
626
627    /// Creates a shallow clone of the node (clones type & data only; no children, detached).
628    pub fn shallow_clone(&mut self, node: NodeId) -> Result<NodeId> {
629        let data = self.arena.get(node).ok_or(XmlError::InvalidNodeId)?.clone();
630        let cloned_kind = match &data.kind {
631            NodeKind::Document => NodeKind::Document,
632            NodeKind::Element(el) => NodeKind::Element(el.clone()),
633            NodeKind::Text(txt) => NodeKind::Text(txt.clone()),
634            NodeKind::Comment(c) => NodeKind::Comment(c.clone()),
635            NodeKind::Declaration(d) => NodeKind::Declaration(d.clone()),
636            NodeKind::Unknown(u) => NodeKind::Unknown(u.clone()),
637        };
638        let cloned_data = NodeData::new(cloned_kind, data.line_num);
639        let cloned_id = self.arena.alloc(cloned_data);
640        Ok(cloned_id)
641    }
642
643    /// Recursively clones a node and all of its descendants.
644    pub fn deep_clone(&mut self, node: NodeId) -> Result<NodeId> {
645        let cloned_id = self.shallow_clone(node)?;
646        let mut next_child = self.first_child(node);
647        while let Some(child) = next_child {
648            let cloned_child = self.deep_clone(child)?;
649            self.insert_end_child(cloned_id, cloned_child)?;
650            next_child = self.next_sibling(child);
651        }
652        Ok(cloned_id)
653    }
654
655    // --- Attribute Manipulation ---
656
657    /// Returns the string value of the attribute on element `el` if it exists.
658    ///
659    /// # Examples
660    ///
661    /// ```
662    /// use tinyxml2::Document;
663    ///
664    /// let doc = Document::parse(r#"<server host="localhost" port="8080"/>"#)?;
665    /// let server = doc.root_element().expect("server element");
666    ///
667    /// assert_eq!(doc.attribute(server, "host"), Some("localhost"));
668    /// assert_eq!(doc.attribute(server, "missing"), None);
669    /// # Ok::<(), tinyxml2::XmlError>(())
670    /// ```
671    #[must_use]
672    pub fn attribute(&self, el: NodeId, name: &str) -> Option<&str> {
673        let data = self.arena.get(el)?;
674        match &data.kind {
675            NodeKind::Element(el_data) | NodeKind::Declaration(el_data) => el_data
676                .attributes
677                .iter()
678                .find(|attr| attr.name == name)
679                .map(|attr| attr.value.as_str()),
680            _ => None,
681        }
682    }
683
684    /// Sets the value of an attribute on element `el` (inserts or updates).
685    ///
686    /// # Examples
687    ///
688    /// ```
689    /// use tinyxml2::Document;
690    ///
691    /// let mut doc = Document::parse("<feature/>")?;
692    /// let feature = doc.root_element().expect("feature element");
693    ///
694    /// doc.set_attribute(feature, "enabled", "true")?;
695    /// assert_eq!(doc.attribute(feature, "enabled"), Some("true"));
696    /// # Ok::<(), tinyxml2::XmlError>(())
697    /// ```
698    pub fn set_attribute(&mut self, el: NodeId, name: &str, value: &str) -> Result<()> {
699        let data = self.arena.get_mut(el).ok_or(XmlError::InvalidNodeId)?;
700        match &mut data.kind {
701            NodeKind::Element(el_data) | NodeKind::Declaration(el_data) => {
702                if let Some(attr) = el_data.attributes.iter_mut().find(|attr| attr.name == name) {
703                    attr.value = value.to_string();
704                } else {
705                    el_data.attributes.push(Attribute {
706                        name: name.to_string(),
707                        value: value.to_string(),
708                    });
709                }
710                Ok(())
711            }
712            _ => Err(XmlError::InvalidNodeId),
713        }
714    }
715
716    /// Removes an attribute from element `el`.
717    pub fn delete_attribute(&mut self, el: NodeId, name: &str) -> Result<()> {
718        let data = self.arena.get_mut(el).ok_or(XmlError::InvalidNodeId)?;
719        match &mut data.kind {
720            NodeKind::Element(el_data) | NodeKind::Declaration(el_data) => {
721                if let Some(pos) = el_data.attributes.iter().position(|attr| attr.name == name) {
722                    el_data.attributes.remove(pos);
723                    Ok(())
724                } else {
725                    Err(XmlError::NoAttribute)
726                }
727            }
728            _ => Err(XmlError::InvalidNodeId),
729        }
730    }
731
732    /// Returns a reference to the first Attribute on element `el`.
733    #[must_use]
734    pub fn first_attribute(&self, el: NodeId) -> Option<&Attribute> {
735        let data = self.arena.get(el)?;
736        match &data.kind {
737            NodeKind::Element(el_data) | NodeKind::Declaration(el_data) => {
738                el_data.attributes.first()
739            }
740            _ => None,
741        }
742    }
743
744    /// Returns the number of attributes on element `el`.
745    #[must_use]
746    pub fn attribute_count(&self, el: NodeId) -> usize {
747        let Some(data) = self.arena.get(el) else {
748            return 0;
749        };
750        match &data.kind {
751            NodeKind::Element(el_data) | NodeKind::Declaration(el_data) => el_data.attributes.len(),
752            _ => 0,
753        }
754    }
755
756    /// Finds a reference to the Attribute with the specified name.
757    #[must_use]
758    pub fn find_attribute(&self, el: NodeId, name: &str) -> Option<&Attribute> {
759        let data = self.arena.get(el)?;
760        match &data.kind {
761            NodeKind::Element(el_data) | NodeKind::Declaration(el_data) => {
762                el_data.attributes.iter().find(|attr| attr.name == name)
763            }
764            _ => None,
765        }
766    }
767
768    /// Returns an iterator over all attributes of element `el`.
769    pub fn iterate_attributes(&self, el: NodeId) -> impl Iterator<Item = &Attribute> {
770        let attrs = match self.arena.get(el) {
771            Some(data) => match &data.kind {
772                NodeKind::Element(el_data) | NodeKind::Declaration(el_data) => {
773                    &el_data.attributes[..]
774                }
775                _ => &[],
776            },
777            None => &[],
778        };
779        attrs.iter()
780    }
781
782    // --- Iterator & Ref Convenience APIs ---
783
784    /// Returns an iterator over the direct children of `parent`.
785    pub fn children(&self, parent: NodeId) -> crate::iter::Children<'_> {
786        crate::iter::Children::new(self, parent)
787    }
788
789    /// Returns an iterator over the direct child elements of `parent`,
790    /// optionally filtered by tag name.
791    ///
792    /// # Examples
793    ///
794    /// ```
795    /// use tinyxml2::Document;
796    ///
797    /// let doc = Document::parse("<catalog><book/><magazine/><book/></catalog>")?;
798    /// let catalog = doc.root_element().expect("catalog element");
799    /// let book_count = doc.child_elements(catalog, Some("book")).count();
800    ///
801    /// assert_eq!(book_count, 2);
802    /// # Ok::<(), tinyxml2::XmlError>(())
803    /// ```
804    pub fn child_elements(
805        &self,
806        parent: NodeId,
807        name: Option<&str>,
808    ) -> crate::iter::ChildElements<'_> {
809        crate::iter::ChildElements::new(self, parent, name)
810    }
811
812    /// Returns an iterator over the following siblings of `node`.
813    pub fn siblings(&self, node: NodeId) -> crate::iter::Siblings<'_> {
814        crate::iter::Siblings::new(self, node)
815    }
816
817    /// Returns a depth-first pre-order iterator over all descendants of `root`.
818    pub fn descendants(&self, root: NodeId) -> crate::iter::Descendants<'_> {
819        crate::iter::Descendants::new(self, root)
820    }
821
822    /// Returns an iterator over the attributes of element `el`.
823    pub fn attributes(&self, el: NodeId) -> crate::iter::Attributes<'_> {
824        let Some(data) = self.arena.get(el) else {
825            return crate::iter::Attributes::empty();
826        };
827        match &data.kind {
828            NodeKind::Element(el_data) | NodeKind::Declaration(el_data) => {
829                crate::iter::Attributes::new(&el_data.attributes)
830            }
831            _ => crate::iter::Attributes::empty(),
832        }
833    }
834
835    /// Creates an immutable navigation [`Handle`](crate::handle::Handle) for the given node.
836    pub fn handle(&self, node: NodeId) -> crate::handle::Handle<'_> {
837        crate::handle::Handle::new(self, node)
838    }
839
840    /// Creates a mutable navigation [`HandleMut`](crate::handle::HandleMut) for the given node.
841    pub fn handle_mut(&mut self, node: NodeId) -> crate::handle::HandleMut<'_> {
842        crate::handle::HandleMut::new(self, node)
843    }
844
845    /// Returns a [`NodeRef`](crate::refs::NodeRef) for the given node, if it exists.
846    pub fn node_ref(&self, id: NodeId) -> Option<crate::refs::NodeRef<'_>> {
847        if self.arena.contains(id) {
848            Some(crate::refs::NodeRef::new(self, id))
849        } else {
850            None
851        }
852    }
853
854    /// Returns an [`ElementRef`](crate::refs::ElementRef) for the given node,
855    /// if it exists and is an Element.
856    pub fn element_ref(&self, id: NodeId) -> Option<crate::refs::ElementRef<'_>> {
857        let data = self.arena.get(id)?;
858        match &data.kind {
859            NodeKind::Element(_) => Some(crate::refs::ElementRef::new(self, id)),
860            _ => None,
861        }
862    }
863
864    // --- Visitor / Traversal APIs ---
865
866    /// Walk the DOM tree starting at the document root, driving the visitor.
867    pub fn accept(&self, visitor: &mut dyn crate::visitor::XmlVisitor) -> bool {
868        self.accept_node(self.root, visitor)
869    }
870
871    /// Walk the DOM tree starting at the specified node, driving the visitor.
872    pub fn accept_node(&self, node: NodeId, visitor: &mut dyn crate::visitor::XmlVisitor) -> bool {
873        if !self.arena.contains(node) {
874            return false;
875        }
876
877        let Some(data) = self.arena.get(node) else {
878            return false;
879        };
880
881        match &data.kind {
882            NodeKind::Document => {
883                if !visitor.visit_enter_document(self) {
884                    return false;
885                }
886                let mut current = self.first_child(node);
887                while let Some(child) = current {
888                    if !self.accept_node(child, visitor) {
889                        return false;
890                    }
891                    current = self.next_sibling(child);
892                }
893                if !visitor.visit_exit_document(self) {
894                    return false;
895                }
896            }
897            NodeKind::Element(_) => {
898                if !visitor.visit_enter_element(self, node) {
899                    return false;
900                }
901                let mut current = self.first_child(node);
902                while let Some(child) = current {
903                    if !self.accept_node(child, visitor) {
904                        return false;
905                    }
906                    current = self.next_sibling(child);
907                }
908                if !visitor.visit_exit_element(self, node) {
909                    return false;
910                }
911            }
912            NodeKind::Text(_) => {
913                if !visitor.visit_text(self, node) {
914                    return false;
915                }
916            }
917            NodeKind::Comment(_) => {
918                if !visitor.visit_comment(self, node) {
919                    return false;
920                }
921            }
922            NodeKind::Declaration(_) => {
923                if !visitor.visit_declaration(self, node) {
924                    return false;
925                }
926            }
927            NodeKind::Unknown(_) => {
928                if !visitor.visit_unknown(self, node) {
929                    return false;
930                }
931            }
932        }
933        true
934    }
935
936    // --- Serialization APIs ---
937
938    /// Pretty-prints the entire document to a String.
939    #[must_use]
940    #[allow(clippy::inherent_to_string_shadow_display)]
941    pub fn to_string(&self) -> String {
942        let mut printer = crate::printer::XmlPrinter::new();
943        self.accept(&mut printer);
944        printer.into_string()
945    }
946
947    /// Compact-prints the entire document to a String.
948    #[must_use]
949    pub fn to_string_compact(&self) -> String {
950        let mut printer = crate::printer::XmlPrinter::new_compact();
951        self.accept(&mut printer);
952        printer.into_string()
953    }
954
955    /// Saves the pretty-printed document to the given file path.
956    #[cfg(feature = "std")]
957    pub fn save_file(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
958        let file = std::fs::File::create(path)?;
959        self.save_writer(file)
960    }
961
962    /// Saves the compact-printed document to the given file path.
963    #[cfg(feature = "std")]
964    pub fn save_file_compact(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
965        let file = std::fs::File::create(path)?;
966        self.save_writer_compact(file)
967    }
968
969    /// Pretty-prints the document to the given `std::io::Write` sink.
970    #[cfg(feature = "std")]
971    pub fn save_writer(&self, mut writer: impl std::io::Write) -> Result<()> {
972        let s = self.to_string();
973        writer.write_all(s.as_bytes())?;
974        Ok(())
975    }
976
977    /// Compact-prints the document to the given `std::io::Write` sink.
978    #[cfg(feature = "std")]
979    pub fn save_writer_compact(&self, mut writer: impl std::io::Write) -> Result<()> {
980        let s = self.to_string_compact();
981        writer.write_all(s.as_bytes())?;
982        Ok(())
983    }
984}
985
986impl Default for Document {
987    fn default() -> Self {
988        Self::new()
989    }
990}
991
992impl fmt::Display for Document {
993    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
994        write!(f, "{}", self.to_string())
995    }
996}