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