Skip to main content

rusty_xml_tree/
lib.rs

1//! Arena DOM matching libxml2 `tree.h` ownership: the document owns every node.
2//! Handles are indices, not parent+child `&mut` pairs.
3
4#![forbid(unsafe_code)]
5
6use std::collections::HashMap;
7
8/// libxml2 `xmlElementType` discriminants.
9#[derive(Clone, Copy, PartialEq, Eq, Debug)]
10#[repr(u32)]
11pub enum NodeKind {
12    Element = 1,
13    Attribute = 2,
14    Text = 3,
15    CData = 4,
16    EntityRef = 5,
17    Entity = 6,
18    Pi = 7,
19    Comment = 8,
20    Document = 9,
21    DocumentType = 10,
22    DocumentFrag = 11,
23    Notation = 12,
24    HtmlDocument = 13,
25    Dtd = 14,
26    ElementDecl = 15,
27    AttributeDecl = 16,
28    EntityDecl = 17,
29    Namespace = 18,
30    XIncludeStart = 19,
31    XIncludeEnd = 20,
32}
33
34/// Parsed DTD attached to a document (`xmlDtd`).
35#[derive(Clone, Debug, Default)]
36pub struct XmlDtd {
37    pub name: Option<String>,
38    pub public_id: Option<String>,
39    pub system_id: Option<String>,
40    pub int_subset: Option<String>,
41    /// General entity name → replacement text.
42    pub entities: HashMap<String, String>,
43    /// Parameter entity name → replacement.
44    pub parameter_entities: HashMap<String, String>,
45    /// Names of entities declared with an NDATA annotation. An attribute of
46    /// type ENTITY must name one of these, which needs them kept apart from
47    /// parsed entities rather than lumped in with them.
48    pub unparsed_entities: std::collections::HashSet<String>,
49    /// Notation names declared by `<!NOTATION>`. We parsed those declarations
50    /// and threw the name away, so nothing could check that an NDATA
51    /// annotation or a NOTATION attribute type names one that exists.
52    pub notations: std::collections::HashSet<String>,
53    /// Notation names referenced by an NDATA annotation, kept so the
54    /// "Notation Declared" constraint can be checked once the whole subset is
55    /// read rather than in declaration order.
56    pub ndata_notations: Vec<String>,
57    /// The internal subset used a parameter entity reference, so the set of
58    /// entity declarations may be incomplete. XML 1.0 4.1 turns "Entity
59    /// Declared" from a well-formedness constraint into a validity one in that
60    /// case: an unresolvable entity must not kill the parse.
61    pub has_parameter_entity_refs: bool,
62    /// Element name → content model.
63    pub elements: HashMap<String, ElementDecl>,
64    /// Element types declared more than once. A map cannot represent that, and
65    /// "Unique Element Type Declaration" is a validity constraint, so it has to
66    /// be recorded as the declarations go by and reported at validation time
67    /// rather than at parse time.
68    pub duplicate_elements: Vec<String>,
69    /// (element, attribute) → declaration.
70    pub attributes: HashMap<(String, String), AttrDecl>,
71}
72
73#[derive(Clone, Debug)]
74pub enum ElementDecl {
75    Empty,
76    Any,
77    Mixed(Vec<String>),
78    Children(String),
79}
80
81#[derive(Clone, Debug)]
82pub struct AttrDecl {
83    pub att_type: String,
84    pub default: AttrDefault,
85    pub default_value: Option<String>,
86    pub enumerated: Vec<String>,
87}
88
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
90pub enum AttrDefault {
91    Required,
92    Implied,
93    Fixed,
94    Value,
95}
96
97/// Stable handle into an [`XmlDoc`] arena. Valid for the lifetime of the doc.
98#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
99pub struct NodeId(pub u32);
100
101impl NodeId {
102    /// Document node is always slot 0.
103    pub const DOCUMENT: NodeId = NodeId(0);
104
105    pub fn index(self) -> usize {
106        self.0 as usize
107    }
108}
109
110
111
112#[derive(Clone, Debug)]
113pub struct Node {
114    pub kind: NodeKind,
115    pub name: String,
116    pub prefix: Option<String>,
117    pub ns_uri: Option<String>,
118    pub content: String,
119    pub parent: Option<NodeId>,
120    pub first_child: Option<NodeId>,
121    pub last_child: Option<NodeId>,
122    pub prev_sibling: Option<NodeId>,
123    pub next_sibling: Option<NodeId>,
124    pub first_attr: Option<NodeId>,
125    pub last_attr: Option<NodeId>,
126    /// Namespace declarations on this element (`xmlns` / `xmlns:prefix`), in source order.
127    pub ns_defs: Vec<(Option<String>, String)>,
128}
129
130impl Node {
131    fn new(kind: NodeKind, name: String) -> Self {
132        Self {
133            kind,
134            name,
135            prefix: None,
136            ns_uri: None,
137            content: String::new(),
138            parent: None,
139            first_child: None,
140            last_child: None,
141            prev_sibling: None,
142            next_sibling: None,
143            first_attr: None,
144            last_attr: None,
145            ns_defs: Vec::new(),
146        }
147    }
148}
149
150/// libxml2 `xmlDoc`.
151#[derive(Clone, Debug)]
152pub struct XmlDoc {
153    nodes: Vec<Node>,
154    /// XML version string; default `"1.0"`.
155    pub version: String,
156    /// Encoding name from the XML declaration, if any.
157    pub encoding: Option<String>,
158    /// `Some(true/false)` from `standalone`, `None` if omitted.
159    pub standalone: Option<bool>,
160    /// First element child of the document (cached; also discoverable by walk).
161    root: Option<NodeId>,
162    /// Internal / attached DTD, if any.
163    pub dtd: Option<XmlDtd>,
164    /// Entity references the parse could not resolve. They are kept as written
165    /// rather than being fatal when the subset is incomplete, and the
166    /// validator reports them.
167    pub undeclared_entity_refs: Vec<String>,
168    /// Text nodes whose content came from a character or entity reference.
169    ///
170    /// Such text is never ignorable whitespace: `<foo><foo/>&#32;<foo/></foo>`
171    /// against an element-only content model is character data where none is
172    /// allowed, and looking only at whether the text is blank cannot tell it
173    /// from the indentation beside it.
174    pub reference_text: std::collections::HashSet<NodeId>,
175    /// Elements whose content included an entity reference, including one that
176    /// expanded to nothing. `<foo>&empty;</foo>` against EMPTY is content, and
177    /// the tree has no node to show for it.
178    pub elements_with_entity_refs: std::collections::HashSet<NodeId>,
179}
180
181impl Default for XmlDoc {
182    fn default() -> Self {
183        Self::xml_new_doc(Some("1.0"))
184    }
185}
186
187impl XmlDoc {
188    /// `xmlNewDoc`.
189    #[doc(alias = "xmlNewDoc")]
190    pub fn xml_new_doc(version: Option<&str>) -> Self {
191        Self::with_node_capacity(version, 1)
192    }
193
194    /// Build a document whose node arena is sized up front. Starting a parse
195    /// used to allocate the arena, push into it, then reallocate on reserve --
196    /// three trips for what one sized allocation does. The document node's
197    /// name is implied by its kind, so it stores no String either.
198    pub fn with_node_capacity(version: Option<&str>, cap: usize) -> Self {
199        const MAX_ARENA_BYTES: usize = 32 << 20;
200        let ceiling = MAX_ARENA_BYTES / std::mem::size_of::<Node>();
201        // A floor of 4 keeps a small document from reallocating its way up
202        // from a single slot, which is what Vec::new()+push used to give it.
203        let mut nodes = Vec::with_capacity(cap.clamp(4, ceiling));
204        nodes.push(Node::new(NodeKind::Document, String::new()));
205        Self {
206            nodes,
207            version: version.unwrap_or("1.0").to_string(),
208            encoding: None,
209            standalone: None,
210            root: None,
211            dtd: None,
212            undeclared_entity_refs: Vec::new(),
213            reference_text: Default::default(),
214            elements_with_entity_refs: Default::default(),
215        }
216    }
217
218    /// Pre-size the node arena. XML runs about one node per 10-15 input bytes,
219    /// so a parser that knows the document length can skip most of the arena's
220    /// doubling-and-copy. Capped so a huge document cannot reserve wildly.
221    pub fn reserve_nodes(&mut self, n: usize) {
222        // Measured node density is ~1 per 10-12 input bytes. The previous cap
223        // of 65_536 was below a 700 KB document's node count, so the arena
224        // still doubled-and-copied its way up -- about 22 MB of memcpy on a
225        // 627 KB file. Shrinking Node itself would need an API break for a
226        // sub-1% effect; reserving correctly removes the same traffic for free.
227        // Cap by memory, not node count: 1<<20 nodes is ~190 MB of arena, which
228        // a large document would commit up front before parsing a byte.
229        const MAX_ARENA_BYTES: usize = 32 << 20;
230        let cap = MAX_ARENA_BYTES / std::mem::size_of::<Node>();
231        self.nodes.reserve(n.min(cap));
232    }
233
234    pub fn node(&self, id: NodeId) -> &Node {
235        &self.nodes[id.index()]
236    }
237
238    pub fn node_mut(&mut self, id: NodeId) -> &mut Node {
239        &mut self.nodes[id.index()]
240    }
241
242    pub fn kind(&self, id: NodeId) -> NodeKind {
243        self.node(id).kind
244    }
245
246    pub fn name(&self, id: NodeId) -> &str {
247        let n = self.node(id);
248        if n.name.is_empty() {
249            // Nodes whose name is fixed by their kind store no String at all;
250            // allocating "#text" once per text node was a measurable share of
251            // every parse. The canonical name is derived here instead.
252            return match n.kind {
253                NodeKind::Text => "#text",
254                NodeKind::CData => "#cdata-section",
255                NodeKind::Comment => "#comment",
256                NodeKind::Document => "#document",
257                _ => "",
258            };
259        }
260        &n.name
261    }
262
263    pub fn prefix(&self, id: NodeId) -> Option<&str> {
264        self.node(id).prefix.as_deref()
265    }
266
267    pub fn ns_uri(&self, id: NodeId) -> Option<&str> {
268        self.node(id).ns_uri.as_deref()
269    }
270
271    pub fn content(&self, id: NodeId) -> &str {
272        &self.node(id).content
273    }
274
275    pub fn parent(&self, id: NodeId) -> Option<NodeId> {
276        self.node(id).parent
277    }
278
279    pub fn first_child(&self, id: NodeId) -> Option<NodeId> {
280        self.node(id).first_child
281    }
282
283    pub fn last_child(&self, id: NodeId) -> Option<NodeId> {
284        self.node(id).last_child
285    }
286
287    pub fn next_sibling(&self, id: NodeId) -> Option<NodeId> {
288        self.node(id).next_sibling
289    }
290
291    pub fn prev_sibling(&self, id: NodeId) -> Option<NodeId> {
292        self.node(id).prev_sibling
293    }
294
295    pub fn first_attr(&self, id: NodeId) -> Option<NodeId> {
296        self.node(id).first_attr
297    }
298
299    pub fn ns_defs(&self, id: NodeId) -> &[(Option<String>, String)] {
300        &self.node(id).ns_defs
301    }
302
303    /// Allocate a node whose name is implied by its kind, storing no String.
304    /// [`XmlDoc::name`] reports the canonical name for these.
305    pub fn alloc_unnamed(&mut self, kind: NodeKind) -> NodeId {
306        let id = NodeId(self.nodes.len() as u32);
307        self.nodes.push(Node::new(kind, String::new()));
308        id
309    }
310
311    pub fn alloc(&mut self, kind: NodeKind, name: impl Into<String>) -> NodeId {
312        let id = NodeId(self.nodes.len() as u32);
313        self.nodes.push(Node::new(kind, name.into()));
314        id
315    }
316
317    /// `xmlDocGetRootElement`.
318    #[doc(alias = "xmlDocGetRootElement")]
319    pub fn xml_doc_get_root_element(&self) -> Option<NodeId> {
320        if let Some(r) = self.root {
321            return Some(r);
322        }
323        let mut c = self.first_child(NodeId::DOCUMENT);
324        while let Some(id) = c {
325            if self.kind(id) == NodeKind::Element {
326                return Some(id);
327            }
328            c = self.next_sibling(id);
329        }
330        None
331    }
332
333    /// `xmlDocSetRootElement`. Returns the previous root, if any.
334    #[doc(alias = "xmlDocSetRootElement")]
335    pub fn xml_doc_set_root_element(&mut self, elem: NodeId) -> Option<NodeId> {
336        let prev = self.xml_doc_get_root_element();
337        if let Some(p) = prev {
338            self.xml_unlink_node(p);
339        }
340        self.xml_add_child(NodeId::DOCUMENT, elem);
341        self.root = Some(elem);
342        prev
343    }
344
345    /// `xmlNewNode`.
346    #[doc(alias = "xmlNewNode")]
347    pub fn xml_new_node(&mut self, ns_uri: Option<&str>, name: &str) -> NodeId {
348        let id = self.alloc(NodeKind::Element, name);
349        self.node_mut(id).ns_uri = ns_uri.map(str::to_string);
350        id
351    }
352
353    /// `xmlNewDocNode`.
354    #[doc(alias = "xmlNewDocNode")]
355    pub fn xml_new_doc_node(
356        &mut self,
357        ns_uri: Option<&str>,
358        name: &str,
359        content: Option<&str>,
360    ) -> NodeId {
361        let id = self.xml_new_node(ns_uri, name);
362        if let Some(c) = content {
363            if !c.is_empty() {
364                let t = self.alloc(NodeKind::Text, "#text");
365                self.node_mut(t).content = c.to_string();
366                self.xml_add_child(id, t);
367            }
368        }
369        id
370    }
371
372    /// `xmlNewChild`.
373    #[doc(alias = "xmlNewChild")]
374    pub fn xml_new_child(
375        &mut self,
376        parent: NodeId,
377        ns_uri: Option<&str>,
378        name: &str,
379        content: Option<&str>,
380    ) -> NodeId {
381        let id = self.xml_new_doc_node(ns_uri, name, content);
382        self.xml_add_child(parent, id);
383        id
384    }
385
386    /// `xmlAddChild`.
387    #[doc(alias = "xmlAddChild")]
388    pub fn xml_add_child(&mut self, parent: NodeId, child: NodeId) {
389        if child == parent {
390            return;
391        }
392        self.xml_unlink_node(child);
393        self.node_mut(child).parent = Some(parent);
394        let last = self.node(parent).last_child;
395        if let Some(l) = last {
396            self.node_mut(l).next_sibling = Some(child);
397            self.node_mut(child).prev_sibling = Some(l);
398        } else {
399            self.node_mut(parent).first_child = Some(child);
400        }
401        self.node_mut(parent).last_child = Some(child);
402        if parent == NodeId::DOCUMENT && self.kind(child) == NodeKind::Element {
403            self.root = Some(child);
404        }
405    }
406
407    /// `xmlAddNextSibling`.
408    #[doc(alias = "xmlAddNextSibling")]
409    pub fn xml_add_next_sibling(&mut self, cur: NodeId, elem: NodeId) {
410        self.xml_unlink_node(elem);
411        let parent = self.node(cur).parent;
412        let next = self.node(cur).next_sibling;
413        self.node_mut(elem).parent = parent;
414        self.node_mut(elem).prev_sibling = Some(cur);
415        self.node_mut(elem).next_sibling = next;
416        self.node_mut(cur).next_sibling = Some(elem);
417        if let Some(n) = next {
418            self.node_mut(n).prev_sibling = Some(elem);
419        } else if let Some(p) = parent {
420            self.node_mut(p).last_child = Some(elem);
421        }
422    }
423
424    /// `xmlAddPrevSibling`.
425    #[doc(alias = "xmlAddPrevSibling")]
426    pub fn xml_add_prev_sibling(&mut self, cur: NodeId, elem: NodeId) {
427        self.xml_unlink_node(elem);
428        let parent = self.node(cur).parent;
429        let prev = self.node(cur).prev_sibling;
430        self.node_mut(elem).parent = parent;
431        self.node_mut(elem).next_sibling = Some(cur);
432        self.node_mut(elem).prev_sibling = prev;
433        self.node_mut(cur).prev_sibling = Some(elem);
434        if let Some(p) = prev {
435            self.node_mut(p).next_sibling = Some(elem);
436        } else if let Some(par) = parent {
437            self.node_mut(par).first_child = Some(elem);
438        }
439    }
440
441    /// `xmlUnlinkNode`.
442    #[doc(alias = "xmlUnlinkNode")]
443    pub fn xml_unlink_node(&mut self, id: NodeId) {
444        if id == NodeId::DOCUMENT {
445            return;
446        }
447        let parent = self.node(id).parent;
448        let prev = self.node(id).prev_sibling;
449        let next = self.node(id).next_sibling;
450        if let Some(p) = prev {
451            self.node_mut(p).next_sibling = next;
452        }
453        if let Some(n) = next {
454            self.node_mut(n).prev_sibling = prev;
455        }
456        if let Some(par) = parent {
457            if self.node(par).first_child == Some(id) {
458                self.node_mut(par).first_child = next;
459            }
460            if self.node(par).last_child == Some(id) {
461                self.node_mut(par).last_child = prev;
462            }
463        }
464        if self.root == Some(id) {
465            self.root = None;
466        }
467        self.node_mut(id).parent = None;
468        self.node_mut(id).prev_sibling = None;
469        self.node_mut(id).next_sibling = None;
470    }
471
472    /// `xmlReplaceNode`.
473    #[doc(alias = "xmlReplaceNode")]
474    pub fn xml_replace_node(&mut self, old: NodeId, new: NodeId) -> NodeId {
475        self.xml_add_next_sibling(old, new);
476        self.xml_unlink_node(old);
477        new
478    }
479
480    /// As [`XmlDoc::add_attr`], but takes ownership. The borrowing form has to
481    /// allocate a fresh String for the name, the prefix and the value, all of
482    /// which the parser already owns.
483    pub fn add_attr_owned(
484        &mut self,
485        elem: NodeId,
486        name: String,
487        prefix: Option<String>,
488        value: String,
489    ) -> NodeId {
490        let id = self.alloc(NodeKind::Attribute, name);
491        self.node_mut(id).prefix = prefix;
492        self.node_mut(id).content = value;
493        self.node_mut(id).parent = Some(elem);
494        let last = self.node(elem).last_attr;
495        if let Some(l) = last {
496            self.node_mut(l).next_sibling = Some(id);
497            self.node_mut(id).prev_sibling = Some(l);
498        } else {
499            self.node_mut(elem).first_attr = Some(id);
500        }
501        self.node_mut(elem).last_attr = Some(id);
502        id
503    }
504
505    pub fn add_attr(&mut self, elem: NodeId, name: &str, prefix: Option<&str>, value: &str) -> NodeId {
506        let id = self.alloc(NodeKind::Attribute, name);
507        self.node_mut(id).prefix = prefix.map(str::to_string);
508        self.node_mut(id).content = value.to_string();
509        self.node_mut(id).parent = Some(elem);
510        let last = self.node(elem).last_attr;
511        if let Some(l) = last {
512            self.node_mut(l).next_sibling = Some(id);
513            self.node_mut(id).prev_sibling = Some(l);
514        } else {
515            self.node_mut(elem).first_attr = Some(id);
516        }
517        self.node_mut(elem).last_attr = Some(id);
518        id
519    }
520
521    pub fn push_ns_def(&mut self, elem: NodeId, prefix: Option<String>, uri: String) {
522        self.node_mut(elem).ns_defs.push((prefix, uri));
523    }
524
525    /// `xmlSetProp`.
526    #[doc(alias = "xmlSetProp")]
527    pub fn xml_set_prop(&mut self, node: NodeId, name: &str, value: &str) -> NodeId {
528        let mut a = self.first_attr(node);
529        while let Some(id) = a {
530            if self.node(id).prefix.is_none() && self.node(id).name == name {
531                self.node_mut(id).content = value.to_string();
532                return id;
533            }
534            a = self.next_sibling(id);
535        }
536        self.add_attr(node, name, None, value)
537    }
538
539    /// `xmlGetProp`.
540    #[doc(alias = "xmlGetProp")]
541    pub fn xml_get_prop(&self, node: NodeId, name: &str) -> Option<String> {
542        let mut a = self.first_attr(node);
543        while let Some(id) = a {
544            if self.node(id).prefix.is_none() && self.node(id).name == name {
545                return Some(self.node(id).content.clone());
546            }
547            a = self.next_sibling(id);
548        }
549        None
550    }
551
552    /// `xmlHasProp`.
553    #[doc(alias = "xmlHasProp")]
554    pub fn xml_has_prop(&self, node: NodeId, name: &str) -> bool {
555        self.xml_get_prop(node, name).is_some()
556    }
557
558    /// `xmlUnsetProp`.
559    #[doc(alias = "xmlUnsetProp")]
560    pub fn xml_unset_prop(&mut self, node: NodeId, name: &str) -> bool {
561        let mut a = self.first_attr(node);
562        let mut prev: Option<NodeId> = None;
563        while let Some(id) = a {
564            let next = self.next_sibling(id);
565            if self.node(id).prefix.is_none() && self.node(id).name == name {
566                if let Some(p) = prev {
567                    self.node_mut(p).next_sibling = next;
568                } else {
569                    self.node_mut(node).first_attr = next;
570                }
571                if next.is_none() {
572                    self.node_mut(node).last_attr = prev;
573                }
574                if let Some(n) = next {
575                    self.node_mut(n).prev_sibling = prev;
576                }
577                self.node_mut(id).parent = None;
578                self.node_mut(id).prev_sibling = None;
579                self.node_mut(id).next_sibling = None;
580                return true;
581            }
582            prev = Some(id);
583            a = next;
584        }
585        false
586    }
587
588    /// `xmlNodeGetContent` — concatenate descendant text/CDATA.
589    #[doc(alias = "xmlNodeGetContent")]
590    pub fn xml_node_get_content(&self, id: NodeId) -> String {
591        match self.kind(id) {
592            NodeKind::Text | NodeKind::CData | NodeKind::Comment | NodeKind::Pi | NodeKind::Attribute => {
593                self.content(id).to_string()
594            }
595            _ => {
596                let mut out = String::new();
597                self.collect_text(id, &mut out);
598                out
599            }
600        }
601    }
602
603    fn collect_text(&self, id: NodeId, out: &mut String) {
604        let mut c = self.first_child(id);
605        while let Some(ch) = c {
606            match self.kind(ch) {
607                NodeKind::Text | NodeKind::CData => out.push_str(self.content(ch)),
608                NodeKind::Element => self.collect_text(ch, out),
609                _ => {}
610            }
611            c = self.next_sibling(ch);
612        }
613    }
614
615    /// `xmlNodeSetContent` — replace children with a single text node.
616    #[doc(alias = "xmlNodeSetContent")]
617    pub fn xml_node_set_content(&mut self, id: NodeId, content: &str) {
618        match self.kind(id) {
619            NodeKind::Text | NodeKind::CData | NodeKind::Comment | NodeKind::Pi | NodeKind::Attribute => {
620                self.node_mut(id).content = content.to_string();
621            }
622            _ => {
623                let mut c = self.first_child(id);
624                while let Some(ch) = c {
625                    let next = self.next_sibling(ch);
626                    self.xml_unlink_node(ch);
627                    c = next;
628                }
629                if !content.is_empty() {
630                    let t = self.alloc(NodeKind::Text, "#text");
631                    self.node_mut(t).content = content.to_string();
632                    self.xml_add_child(id, t);
633                }
634            }
635        }
636    }
637
638    /// `xmlIsBlankNode`.
639    #[doc(alias = "xmlIsBlankNode")]
640    pub fn xml_is_blank_node(&self, id: NodeId) -> bool {
641        match self.kind(id) {
642            NodeKind::Text | NodeKind::CData => self.content(id).chars().all(|c| {
643                c == ' ' || c == '\t' || c == '\n' || c == '\r'
644            }),
645            _ => false,
646        }
647    }
648
649    /// `xmlSearchNs` — walk ancestors for a prefix binding.
650    #[doc(alias = "xmlSearchNs")]
651    pub fn xml_search_ns(&self, node: NodeId, prefix: Option<&str>) -> Option<String> {
652        if prefix == Some("xml") {
653            return Some("http://www.w3.org/XML/1998/namespace".into());
654        }
655        if prefix == Some("xmlns") {
656            return Some("http://www.w3.org/2000/xmlns/".into());
657        }
658        let mut cur = Some(node);
659        while let Some(id) = cur {
660            for (p, uri) in self.ns_defs(id) {
661                if p.as_deref() == prefix {
662                    return Some(uri.clone());
663                }
664            }
665            cur = self.parent(id);
666        }
667        None
668    }
669
670    /// `xmlNewNs` — add a namespace declaration on an element.
671    #[doc(alias = "xmlNewNs")]
672    pub fn xml_new_ns(&mut self, node: NodeId, href: &str, prefix: Option<&str>) {
673        self.push_ns_def(node, prefix.map(str::to_string), href.to_string());
674    }
675
676    /// `xmlSetNs`.
677    #[doc(alias = "xmlSetNs")]
678    pub fn xml_set_ns(&mut self, node: NodeId, href: Option<&str>, prefix: Option<&str>) {
679        self.node_mut(node).ns_uri = href.map(str::to_string);
680        self.node_mut(node).prefix = prefix.map(str::to_string);
681    }
682
683    /// `xmlCopyDoc` — deep copy.
684    #[doc(alias = "xmlCopyDoc")]
685    pub fn xml_copy_doc(&self) -> XmlDoc {
686        self.clone()
687    }
688
689    pub fn qname(&self, id: NodeId) -> String {
690        match self.prefix(id) {
691            Some(p) => format!("{}:{}", p, self.name(id)),
692            None => self.name(id).to_string(),
693        }
694    }
695
696    pub fn children(&self, id: NodeId) -> NodeIter<'_> {
697        NodeIter {
698            doc: self,
699            next: self.first_child(id),
700        }
701    }
702
703    pub fn attrs(&self, id: NodeId) -> NodeIter<'_> {
704        NodeIter {
705            doc: self,
706            next: self.first_attr(id),
707        }
708    }
709
710    pub fn len(&self) -> usize {
711        self.nodes.len()
712    }
713}
714
715/// Sibling iterator.
716pub struct NodeIter<'a> {
717    doc: &'a XmlDoc,
718    next: Option<NodeId>,
719}
720
721impl Iterator for NodeIter<'_> {
722    type Item = NodeId;
723
724    fn next(&mut self) -> Option<Self::Item> {
725        let n = self.next?;
726        self.next = self.doc.next_sibling(n);
727        Some(n)
728    }
729}
730
731/// `xmlFreeDoc` is `Drop`.
732#[doc(alias = "xmlFreeDoc")]
733pub fn xml_free_doc(_doc: XmlDoc) {}
734
735impl XmlDoc {
736    /// Deep-copy every child of `src_parent` in `src` under `dst_parent` here.
737    ///
738    /// Needed because an entity whose replacement text contains markup has to
739    /// become NODES, not the escaped text of that markup. `<!ENTITY e
740    /// "<b>x</b>">` used in content produced the literal string `<b>x</b>` in
741    /// the tree, so anything reading the document for structure got garbage
742    /// and DTD validation saw character data where an element was declared.
743    ///
744    /// Iterative, like every other traversal here: the replacement is
745    /// attacker-supplied and may be arbitrarily deep.
746    pub fn xml_copy_children_from(
747        &mut self,
748        src: &XmlDoc,
749        src_parent: NodeId,
750        dst_parent: NodeId,
751    ) {
752        // (source node, destination parent), pushed so they pop in order.
753        let mut stack: Vec<(NodeId, NodeId)> = Vec::new();
754        let mut c = src.last_child(src_parent);
755        while let Some(x) = c {
756            stack.push((x, dst_parent));
757            c = src.prev_sibling(x);
758        }
759        while let Some((s, parent)) = stack.pop() {
760            let n = src.node(s);
761            let copy = self.alloc(n.kind, n.name.clone());
762            {
763                let d = self.node_mut(copy);
764                d.prefix = n.prefix.clone();
765                d.ns_uri = n.ns_uri.clone();
766                d.content = n.content.clone();
767                d.ns_defs = n.ns_defs.clone();
768            }
769            self.xml_add_child(parent, copy);
770            // Carry the source's own judgement across rather than marking
771            // everything: a literal space in an entity's replacement is still
772            // ignorable whitespace, only a character reference written out in
773            // that replacement is not.
774            if src.reference_text.contains(&s) {
775                self.reference_text.insert(copy);
776            }
777            // Attributes are a separate chain, not children.
778            let mut a = src.first_attr(s);
779            while let Some(x) = a {
780                let an = src.node(x);
781                let (nm, pre, val, uri) = (
782                    an.name.clone(),
783                    an.prefix.clone(),
784                    an.content.clone(),
785                    an.ns_uri.clone(),
786                );
787                let ac = self.add_attr_owned(copy, nm, pre, val);
788                self.node_mut(ac).ns_uri = uri;
789                a = src.next_sibling(x);
790            }
791            let mut k = src.last_child(s);
792            while let Some(x) = k {
793                stack.push((x, copy));
794                k = src.prev_sibling(x);
795            }
796        }
797    }
798}