···1//! DOM tree, nodes, and events.
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
···1//! DOM tree, nodes, and events.
2+//!
3+//! Arena-based DOM tree with Document, Element, Text, and Comment node types.
4+//! Each node is stored in a flat `Vec` and referenced by `NodeId`.
5+6+use std::fmt;
7+8+/// A handle to a node in the DOM tree.
9+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10+pub struct NodeId(usize);
11+12+impl NodeId {
13+ /// Returns the underlying index.
14+ pub fn index(self) -> usize {
15+ self.0
16+ }
17+}
18+19+/// An HTML/XML attribute (name-value pair).
20+#[derive(Debug, Clone, PartialEq, Eq)]
21+pub struct Attribute {
22+ pub name: String,
23+ pub value: String,
24+}
25+26+/// The data specific to each node type.
27+#[derive(Debug, Clone, PartialEq, Eq)]
28+pub enum NodeData {
29+ /// The root document node.
30+ Document,
31+ /// An element node with tag name, attributes, and optional namespace.
32+ Element {
33+ tag_name: String,
34+ attributes: Vec<Attribute>,
35+ namespace: Option<String>,
36+ },
37+ /// A text node containing character data.
38+ Text { data: String },
39+ /// A comment node.
40+ Comment { data: String },
41+}
42+43+/// A node in the DOM tree, with links to parent, children, and siblings.
44+#[derive(Debug)]
45+struct Node {
46+ data: NodeData,
47+ parent: Option<NodeId>,
48+ first_child: Option<NodeId>,
49+ last_child: Option<NodeId>,
50+ next_sibling: Option<NodeId>,
51+ prev_sibling: Option<NodeId>,
52+}
53+54+/// The DOM document: an arena of nodes with a root document node.
55+pub struct Document {
56+ nodes: Vec<Node>,
57+ root: NodeId,
58+}
59+60+impl fmt::Debug for Document {
61+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62+ f.debug_struct("Document")
63+ .field("node_count", &self.nodes.len())
64+ .field("root", &self.root)
65+ .finish()
66+ }
67+}
68+69+impl Document {
70+ /// Create a new document with a root Document node.
71+ pub fn new() -> Self {
72+ let root_node = Node {
73+ data: NodeData::Document,
74+ parent: None,
75+ first_child: None,
76+ last_child: None,
77+ next_sibling: None,
78+ prev_sibling: None,
79+ };
80+ Document {
81+ nodes: vec![root_node],
82+ root: NodeId(0),
83+ }
84+ }
85+86+ /// Returns the root document node ID.
87+ pub fn root(&self) -> NodeId {
88+ self.root
89+ }
90+91+ /// Create an element node. Returns its `NodeId` (not yet attached to the tree).
92+ pub fn create_element(&mut self, tag_name: &str) -> NodeId {
93+ self.create_element_ns(tag_name, None)
94+ }
95+96+ /// Create an element node with an optional namespace.
97+ pub fn create_element_ns(&mut self, tag_name: &str, namespace: Option<&str>) -> NodeId {
98+ self.push_node(NodeData::Element {
99+ tag_name: tag_name.to_string(),
100+ attributes: Vec::new(),
101+ namespace: namespace.map(|s| s.to_string()),
102+ })
103+ }
104+105+ /// Create a text node. Returns its `NodeId` (not yet attached to the tree).
106+ pub fn create_text(&mut self, data: &str) -> NodeId {
107+ self.push_node(NodeData::Text {
108+ data: data.to_string(),
109+ })
110+ }
111+112+ /// Create a comment node. Returns its `NodeId` (not yet attached to the tree).
113+ pub fn create_comment(&mut self, data: &str) -> NodeId {
114+ self.push_node(NodeData::Comment {
115+ data: data.to_string(),
116+ })
117+ }
118+119+ /// Append `child` as the last child of `parent`.
120+ ///
121+ /// If `child` is already attached elsewhere, it is first removed from its
122+ /// current position.
123+ pub fn append_child(&mut self, parent: NodeId, child: NodeId) {
124+ self.detach(child);
125+126+ let old_last = self.nodes[parent.0].last_child;
127+128+ self.nodes[child.0].parent = Some(parent);
129+ self.nodes[child.0].prev_sibling = old_last;
130+ self.nodes[child.0].next_sibling = None;
131+132+ if let Some(old_last_id) = old_last {
133+ self.nodes[old_last_id.0].next_sibling = Some(child);
134+ } else {
135+ self.nodes[parent.0].first_child = Some(child);
136+ }
137+138+ self.nodes[parent.0].last_child = Some(child);
139+ }
140+141+ /// Insert `new_child` before `reference` under `parent`.
142+ ///
143+ /// If `new_child` is already attached elsewhere, it is first removed.
144+ /// Panics if `reference` is not a child of `parent`.
145+ pub fn insert_before(&mut self, parent: NodeId, new_child: NodeId, reference: NodeId) {
146+ assert_eq!(
147+ self.nodes[reference.0].parent,
148+ Some(parent),
149+ "reference node is not a child of parent"
150+ );
151+152+ self.detach(new_child);
153+154+ let prev = self.nodes[reference.0].prev_sibling;
155+156+ self.nodes[new_child.0].parent = Some(parent);
157+ self.nodes[new_child.0].next_sibling = Some(reference);
158+ self.nodes[new_child.0].prev_sibling = prev;
159+160+ self.nodes[reference.0].prev_sibling = Some(new_child);
161+162+ if let Some(prev_id) = prev {
163+ self.nodes[prev_id.0].next_sibling = Some(new_child);
164+ } else {
165+ self.nodes[parent.0].first_child = Some(new_child);
166+ }
167+ }
168+169+ /// Remove `child` from `parent`.
170+ ///
171+ /// Panics if `child` is not a child of `parent`.
172+ pub fn remove_child(&mut self, parent: NodeId, child: NodeId) {
173+ assert_eq!(
174+ self.nodes[child.0].parent,
175+ Some(parent),
176+ "node is not a child of parent"
177+ );
178+ self.detach(child);
179+ }
180+181+ /// Returns the parent of `node`, or `None` for the root.
182+ pub fn parent(&self, node: NodeId) -> Option<NodeId> {
183+ self.nodes[node.0].parent
184+ }
185+186+ /// Returns an iterator over the direct children of `node`.
187+ pub fn children(&self, node: NodeId) -> Children<'_> {
188+ Children {
189+ doc: self,
190+ next: self.nodes[node.0].first_child,
191+ }
192+ }
193+194+ /// Returns the first child of `node`, if any.
195+ pub fn first_child(&self, node: NodeId) -> Option<NodeId> {
196+ self.nodes[node.0].first_child
197+ }
198+199+ /// Returns the last child of `node`, if any.
200+ pub fn last_child(&self, node: NodeId) -> Option<NodeId> {
201+ self.nodes[node.0].last_child
202+ }
203+204+ /// Returns the next sibling of `node`, if any.
205+ pub fn next_sibling(&self, node: NodeId) -> Option<NodeId> {
206+ self.nodes[node.0].next_sibling
207+ }
208+209+ /// Returns the previous sibling of `node`, if any.
210+ pub fn prev_sibling(&self, node: NodeId) -> Option<NodeId> {
211+ self.nodes[node.0].prev_sibling
212+ }
213+214+ /// Returns a reference to the node's data.
215+ pub fn node_data(&self, node: NodeId) -> &NodeData {
216+ &self.nodes[node.0].data
217+ }
218+219+ /// Returns the tag name if `node` is an Element, or `None`.
220+ pub fn tag_name(&self, node: NodeId) -> Option<&str> {
221+ match &self.nodes[node.0].data {
222+ NodeData::Element { tag_name, .. } => Some(tag_name),
223+ _ => None,
224+ }
225+ }
226+227+ /// Get an attribute value by name. Returns `None` if the node is not
228+ /// an element or the attribute is not present.
229+ pub fn get_attribute(&self, node: NodeId, name: &str) -> Option<&str> {
230+ match &self.nodes[node.0].data {
231+ NodeData::Element { attributes, .. } => attributes
232+ .iter()
233+ .find(|a| a.name == name)
234+ .map(|a| a.value.as_str()),
235+ _ => None,
236+ }
237+ }
238+239+ /// Set an attribute on an element node. If the attribute already exists,
240+ /// its value is replaced. Does nothing if `node` is not an element.
241+ pub fn set_attribute(&mut self, node: NodeId, name: &str, value: &str) {
242+ if let NodeData::Element { attributes, .. } = &mut self.nodes[node.0].data {
243+ if let Some(attr) = attributes.iter_mut().find(|a| a.name == name) {
244+ attr.value = value.to_string();
245+ } else {
246+ attributes.push(Attribute {
247+ name: name.to_string(),
248+ value: value.to_string(),
249+ });
250+ }
251+ }
252+ }
253+254+ /// Remove an attribute from an element node. Returns `true` if the
255+ /// attribute was present.
256+ pub fn remove_attribute(&mut self, node: NodeId, name: &str) -> bool {
257+ if let NodeData::Element { attributes, .. } = &mut self.nodes[node.0].data {
258+ let len_before = attributes.len();
259+ attributes.retain(|a| a.name != name);
260+ attributes.len() < len_before
261+ } else {
262+ false
263+ }
264+ }
265+266+ /// Returns the text content of a Text or Comment node, or `None` for
267+ /// other node types.
268+ pub fn text_content(&self, node: NodeId) -> Option<&str> {
269+ match &self.nodes[node.0].data {
270+ NodeData::Text { data } | NodeData::Comment { data } => Some(data),
271+ _ => None,
272+ }
273+ }
274+275+ /// Set the text content of a Text or Comment node.
276+ /// Does nothing for other node types.
277+ pub fn set_text_content(&mut self, node: NodeId, new_data: &str) {
278+ match &mut self.nodes[node.0].data {
279+ NodeData::Text { data } | NodeData::Comment { data } => {
280+ *data = new_data.to_string();
281+ }
282+ _ => {}
283+ }
284+ }
285+286+ /// Returns the total number of nodes in the arena (including detached nodes).
287+ pub fn len(&self) -> usize {
288+ self.nodes.len()
289+ }
290+291+ /// Returns true if the document has no nodes besides the root.
292+ pub fn is_empty(&self) -> bool {
293+ self.nodes.len() == 1
294+ }
295+296+ // --- private helpers ---
297+298+ fn push_node(&mut self, data: NodeData) -> NodeId {
299+ let id = NodeId(self.nodes.len());
300+ self.nodes.push(Node {
301+ data,
302+ parent: None,
303+ first_child: None,
304+ last_child: None,
305+ next_sibling: None,
306+ prev_sibling: None,
307+ });
308+ id
309+ }
310+311+ /// Detach a node from its current parent (if any), updating sibling links.
312+ fn detach(&mut self, node: NodeId) {
313+ let parent = match self.nodes[node.0].parent {
314+ Some(p) => p,
315+ None => return,
316+ };
317+318+ let prev = self.nodes[node.0].prev_sibling;
319+ let next = self.nodes[node.0].next_sibling;
320+321+ if let Some(prev_id) = prev {
322+ self.nodes[prev_id.0].next_sibling = next;
323+ } else {
324+ self.nodes[parent.0].first_child = next;
325+ }
326+327+ if let Some(next_id) = next {
328+ self.nodes[next_id.0].prev_sibling = prev;
329+ } else {
330+ self.nodes[parent.0].last_child = prev;
331+ }
332+333+ self.nodes[node.0].parent = None;
334+ self.nodes[node.0].prev_sibling = None;
335+ self.nodes[node.0].next_sibling = None;
336+ }
337+}
338+339+impl Default for Document {
340+ fn default() -> Self {
341+ Self::new()
342+ }
343+}
344+345+/// Iterator over the direct children of a node.
346+pub struct Children<'a> {
347+ doc: &'a Document,
348+ next: Option<NodeId>,
349+}
350+351+impl<'a> Iterator for Children<'a> {
352+ type Item = NodeId;
353+354+ fn next(&mut self) -> Option<NodeId> {
355+ let current = self.next?;
356+ self.next = self.doc.nodes[current.0].next_sibling;
357+ Some(current)
358+ }
359+}
360+361+#[cfg(test)]
362+mod tests {
363+ use super::*;
364+365+ #[test]
366+ fn new_document_has_root() {
367+ let doc = Document::new();
368+ assert_eq!(doc.root(), NodeId(0));
369+ assert_eq!(*doc.node_data(doc.root()), NodeData::Document);
370+ assert!(doc.children(doc.root()).next().is_none());
371+ }
372+373+ #[test]
374+ fn create_element() {
375+ let mut doc = Document::new();
376+ let div = doc.create_element("div");
377+ assert_eq!(doc.tag_name(div), Some("div"));
378+ assert!(doc.parent(div).is_none());
379+ }
380+381+ #[test]
382+ fn create_element_with_namespace() {
383+ let mut doc = Document::new();
384+ let svg = doc.create_element_ns("svg", Some("http://www.w3.org/2000/svg"));
385+ match doc.node_data(svg) {
386+ NodeData::Element { namespace, .. } => {
387+ assert_eq!(namespace.as_deref(), Some("http://www.w3.org/2000/svg"));
388+ }
389+ _ => panic!("expected element"),
390+ }
391+ }
392+393+ #[test]
394+ fn create_text() {
395+ let mut doc = Document::new();
396+ let t = doc.create_text("hello");
397+ assert_eq!(doc.text_content(t), Some("hello"));
398+ assert_eq!(doc.tag_name(t), None);
399+ }
400+401+ #[test]
402+ fn create_comment() {
403+ let mut doc = Document::new();
404+ let c = doc.create_comment("a comment");
405+ assert_eq!(doc.text_content(c), Some("a comment"));
406+ match doc.node_data(c) {
407+ NodeData::Comment { data } => assert_eq!(data, "a comment"),
408+ _ => panic!("expected comment"),
409+ }
410+ }
411+412+ #[test]
413+ fn append_child_single() {
414+ let mut doc = Document::new();
415+ let root = doc.root();
416+ let child = doc.create_element("div");
417+ doc.append_child(root, child);
418+419+ assert_eq!(doc.parent(child), Some(root));
420+ assert_eq!(doc.first_child(root), Some(child));
421+ assert_eq!(doc.last_child(root), Some(child));
422+ assert!(doc.next_sibling(child).is_none());
423+ assert!(doc.prev_sibling(child).is_none());
424+ }
425+426+ #[test]
427+ fn append_child_multiple() {
428+ let mut doc = Document::new();
429+ let root = doc.root();
430+ let a = doc.create_element("a");
431+ let b = doc.create_element("b");
432+ let c = doc.create_element("c");
433+ doc.append_child(root, a);
434+ doc.append_child(root, b);
435+ doc.append_child(root, c);
436+437+ assert_eq!(doc.first_child(root), Some(a));
438+ assert_eq!(doc.last_child(root), Some(c));
439+440+ assert_eq!(doc.next_sibling(a), Some(b));
441+ assert_eq!(doc.next_sibling(b), Some(c));
442+ assert!(doc.next_sibling(c).is_none());
443+444+ assert!(doc.prev_sibling(a).is_none());
445+ assert_eq!(doc.prev_sibling(b), Some(a));
446+ assert_eq!(doc.prev_sibling(c), Some(b));
447+ }
448+449+ #[test]
450+ fn children_iterator() {
451+ let mut doc = Document::new();
452+ let root = doc.root();
453+ let a = doc.create_element("a");
454+ let b = doc.create_element("b");
455+ let c = doc.create_element("c");
456+ doc.append_child(root, a);
457+ doc.append_child(root, b);
458+ doc.append_child(root, c);
459+460+ let children: Vec<NodeId> = doc.children(root).collect();
461+ assert_eq!(children, vec![a, b, c]);
462+ }
463+464+ #[test]
465+ fn children_iterator_empty() {
466+ let doc = Document::new();
467+ let children: Vec<NodeId> = doc.children(doc.root()).collect();
468+ assert!(children.is_empty());
469+ }
470+471+ #[test]
472+ fn insert_before_first() {
473+ let mut doc = Document::new();
474+ let root = doc.root();
475+ let a = doc.create_element("a");
476+ let b = doc.create_element("b");
477+ doc.append_child(root, b);
478+ doc.insert_before(root, a, b);
479+480+ let children: Vec<NodeId> = doc.children(root).collect();
481+ assert_eq!(children, vec![a, b]);
482+ assert_eq!(doc.first_child(root), Some(a));
483+ assert_eq!(doc.prev_sibling(b), Some(a));
484+ assert_eq!(doc.next_sibling(a), Some(b));
485+ }
486+487+ #[test]
488+ fn insert_before_middle() {
489+ let mut doc = Document::new();
490+ let root = doc.root();
491+ let a = doc.create_element("a");
492+ let b = doc.create_element("b");
493+ let c = doc.create_element("c");
494+ doc.append_child(root, a);
495+ doc.append_child(root, c);
496+ doc.insert_before(root, b, c);
497+498+ let children: Vec<NodeId> = doc.children(root).collect();
499+ assert_eq!(children, vec![a, b, c]);
500+ }
501+502+ #[test]
503+ fn remove_child_only() {
504+ let mut doc = Document::new();
505+ let root = doc.root();
506+ let child = doc.create_element("div");
507+ doc.append_child(root, child);
508+ doc.remove_child(root, child);
509+510+ assert!(doc.parent(child).is_none());
511+ assert!(doc.first_child(root).is_none());
512+ assert!(doc.last_child(root).is_none());
513+ }
514+515+ #[test]
516+ fn remove_child_first() {
517+ let mut doc = Document::new();
518+ let root = doc.root();
519+ let a = doc.create_element("a");
520+ let b = doc.create_element("b");
521+ doc.append_child(root, a);
522+ doc.append_child(root, b);
523+ doc.remove_child(root, a);
524+525+ assert_eq!(doc.first_child(root), Some(b));
526+ assert_eq!(doc.last_child(root), Some(b));
527+ assert!(doc.prev_sibling(b).is_none());
528+ }
529+530+ #[test]
531+ fn remove_child_last() {
532+ let mut doc = Document::new();
533+ let root = doc.root();
534+ let a = doc.create_element("a");
535+ let b = doc.create_element("b");
536+ doc.append_child(root, a);
537+ doc.append_child(root, b);
538+ doc.remove_child(root, b);
539+540+ assert_eq!(doc.first_child(root), Some(a));
541+ assert_eq!(doc.last_child(root), Some(a));
542+ assert!(doc.next_sibling(a).is_none());
543+ }
544+545+ #[test]
546+ fn remove_child_middle() {
547+ let mut doc = Document::new();
548+ let root = doc.root();
549+ let a = doc.create_element("a");
550+ let b = doc.create_element("b");
551+ let c = doc.create_element("c");
552+ doc.append_child(root, a);
553+ doc.append_child(root, b);
554+ doc.append_child(root, c);
555+ doc.remove_child(root, b);
556+557+ let children: Vec<NodeId> = doc.children(root).collect();
558+ assert_eq!(children, vec![a, c]);
559+ assert_eq!(doc.next_sibling(a), Some(c));
560+ assert_eq!(doc.prev_sibling(c), Some(a));
561+ }
562+563+ #[test]
564+ fn append_child_moves_from_old_parent() {
565+ let mut doc = Document::new();
566+ let root = doc.root();
567+ let parent1 = doc.create_element("div");
568+ let parent2 = doc.create_element("span");
569+ let child = doc.create_element("p");
570+ doc.append_child(root, parent1);
571+ doc.append_child(root, parent2);
572+ doc.append_child(parent1, child);
573+574+ assert_eq!(doc.parent(child), Some(parent1));
575+576+ // Move child from parent1 to parent2.
577+ doc.append_child(parent2, child);
578+579+ assert_eq!(doc.parent(child), Some(parent2));
580+ assert!(doc.first_child(parent1).is_none());
581+ assert_eq!(doc.first_child(parent2), Some(child));
582+ }
583+584+ #[test]
585+ fn set_and_get_attribute() {
586+ let mut doc = Document::new();
587+ let div = doc.create_element("div");
588+589+ assert!(doc.get_attribute(div, "class").is_none());
590+591+ doc.set_attribute(div, "class", "container");
592+ assert_eq!(doc.get_attribute(div, "class"), Some("container"));
593+594+ doc.set_attribute(div, "id", "main");
595+ assert_eq!(doc.get_attribute(div, "id"), Some("main"));
596+ assert_eq!(doc.get_attribute(div, "class"), Some("container"));
597+ }
598+599+ #[test]
600+ fn set_attribute_replaces() {
601+ let mut doc = Document::new();
602+ let div = doc.create_element("div");
603+ doc.set_attribute(div, "class", "old");
604+ doc.set_attribute(div, "class", "new");
605+ assert_eq!(doc.get_attribute(div, "class"), Some("new"));
606+ }
607+608+ #[test]
609+ fn remove_attribute() {
610+ let mut doc = Document::new();
611+ let div = doc.create_element("div");
612+ doc.set_attribute(div, "class", "x");
613+ assert!(doc.remove_attribute(div, "class"));
614+ assert!(doc.get_attribute(div, "class").is_none());
615+ assert!(!doc.remove_attribute(div, "class"));
616+ }
617+618+ #[test]
619+ fn attribute_on_non_element_is_noop() {
620+ let mut doc = Document::new();
621+ let text = doc.create_text("hello");
622+ doc.set_attribute(text, "class", "x");
623+ assert!(doc.get_attribute(text, "class").is_none());
624+ assert!(!doc.remove_attribute(text, "class"));
625+ }
626+627+ #[test]
628+ fn text_content_set() {
629+ let mut doc = Document::new();
630+ let t = doc.create_text("hello");
631+ doc.set_text_content(t, "world");
632+ assert_eq!(doc.text_content(t), Some("world"));
633+ }
634+635+ #[test]
636+ fn text_content_on_element_is_none() {
637+ let mut doc = Document::new();
638+ let div = doc.create_element("div");
639+ assert!(doc.text_content(div).is_none());
640+ }
641+642+ #[test]
643+ fn build_simple_html_tree() {
644+ // Build: <html><head><title>Test</title></head><body><p>Hello</p></body></html>
645+ let mut doc = Document::new();
646+ let root = doc.root();
647+648+ let html = doc.create_element("html");
649+ let head = doc.create_element("head");
650+ let title = doc.create_element("title");
651+ let title_text = doc.create_text("Test");
652+ let body = doc.create_element("body");
653+ let p = doc.create_element("p");
654+ let p_text = doc.create_text("Hello");
655+656+ doc.append_child(root, html);
657+ doc.append_child(html, head);
658+ doc.append_child(head, title);
659+ doc.append_child(title, title_text);
660+ doc.append_child(html, body);
661+ doc.append_child(body, p);
662+ doc.append_child(p, p_text);
663+664+ // Verify structure.
665+ assert_eq!(doc.tag_name(html), Some("html"));
666+ assert_eq!(doc.parent(html), Some(root));
667+668+ let html_children: Vec<NodeId> = doc.children(html).collect();
669+ assert_eq!(html_children, vec![head, body]);
670+671+ let head_children: Vec<NodeId> = doc.children(head).collect();
672+ assert_eq!(head_children, vec![title]);
673+674+ let title_children: Vec<NodeId> = doc.children(title).collect();
675+ assert_eq!(title_children, vec![title_text]);
676+ assert_eq!(doc.text_content(title_text), Some("Test"));
677+678+ let body_children: Vec<NodeId> = doc.children(body).collect();
679+ assert_eq!(body_children, vec![p]);
680+681+ let p_children: Vec<NodeId> = doc.children(p).collect();
682+ assert_eq!(p_children, vec![p_text]);
683+ assert_eq!(doc.text_content(p_text), Some("Hello"));
684+ }
685+686+ #[test]
687+ fn build_tree_with_attributes() {
688+ let mut doc = Document::new();
689+ let root = doc.root();
690+691+ let a = doc.create_element("a");
692+ doc.set_attribute(a, "href", "https://example.com");
693+ doc.set_attribute(a, "class", "link");
694+ doc.append_child(root, a);
695+696+ let text = doc.create_text("Click here");
697+ doc.append_child(a, text);
698+699+ assert_eq!(doc.get_attribute(a, "href"), Some("https://example.com"));
700+ assert_eq!(doc.get_attribute(a, "class"), Some("link"));
701+ assert_eq!(doc.text_content(text), Some("Click here"));
702+ }
703+704+ #[test]
705+ fn nested_children_traversal() {
706+ // <div><span><em>deep</em></span></div>
707+ let mut doc = Document::new();
708+ let root = doc.root();
709+710+ let div = doc.create_element("div");
711+ let span = doc.create_element("span");
712+ let em = doc.create_element("em");
713+ let text = doc.create_text("deep");
714+715+ doc.append_child(root, div);
716+ doc.append_child(div, span);
717+ doc.append_child(span, em);
718+ doc.append_child(em, text);
719+720+ // Walk down.
721+ let mut current = doc.first_child(root).unwrap();
722+ assert_eq!(doc.tag_name(current), Some("div"));
723+724+ current = doc.first_child(current).unwrap();
725+ assert_eq!(doc.tag_name(current), Some("span"));
726+727+ current = doc.first_child(current).unwrap();
728+ assert_eq!(doc.tag_name(current), Some("em"));
729+730+ let leaf = doc.first_child(current).unwrap();
731+ assert_eq!(doc.text_content(leaf), Some("deep"));
732+733+ // Walk back up.
734+ assert_eq!(doc.parent(leaf).map(|n| doc.tag_name(n)), Some(Some("em")));
735+ }
736+737+ #[test]
738+ fn document_len_and_is_empty() {
739+ let doc = Document::new();
740+ assert_eq!(doc.len(), 1); // root only
741+ assert!(doc.is_empty()); // no children besides root
742+743+ let mut doc2 = Document::new();
744+ let _ = doc2.create_element("div");
745+ assert_eq!(doc2.len(), 2);
746+ assert!(!doc2.is_empty());
747+ }
748+749+ #[test]
750+ fn default_document() {
751+ let doc = Document::default();
752+ assert_eq!(doc.root(), NodeId(0));
753+ }
754+755+ #[test]
756+ fn node_id_equality() {
757+ let id1 = NodeId(5);
758+ let id2 = NodeId(5);
759+ let id3 = NodeId(6);
760+ assert_eq!(id1, id2);
761+ assert_ne!(id1, id3);
762+ }
763+764+ #[test]
765+ #[should_panic(expected = "reference node is not a child of parent")]
766+ fn insert_before_wrong_parent_panics() {
767+ let mut doc = Document::new();
768+ let root = doc.root();
769+ let a = doc.create_element("a");
770+ let b = doc.create_element("b");
771+ let c = doc.create_element("c");
772+ doc.append_child(root, a);
773+ // b is not a child of root, so this should panic.
774+ doc.insert_before(root, c, b);
775+ }
776+777+ #[test]
778+ #[should_panic(expected = "node is not a child of parent")]
779+ fn remove_child_wrong_parent_panics() {
780+ let mut doc = Document::new();
781+ let root = doc.root();
782+ let a = doc.create_element("a");
783+ // a is not attached to root.
784+ doc.remove_child(root, a);
785+ }
786+787+ #[test]
788+ fn insert_before_moves_from_old_parent() {
789+ let mut doc = Document::new();
790+ let root = doc.root();
791+ let parent1 = doc.create_element("div");
792+ let parent2 = doc.create_element("span");
793+ let child = doc.create_element("p");
794+ let reference = doc.create_element("em");
795+796+ doc.append_child(root, parent1);
797+ doc.append_child(root, parent2);
798+ doc.append_child(parent1, child);
799+ doc.append_child(parent2, reference);
800+801+ // Move child from parent1 to parent2 before reference.
802+ doc.insert_before(parent2, child, reference);
803+804+ assert_eq!(doc.parent(child), Some(parent2));
805+ assert!(doc.first_child(parent1).is_none());
806+ let children: Vec<NodeId> = doc.children(parent2).collect();
807+ assert_eq!(children, vec![child, reference]);
808+ }
809+810+ #[test]
811+ fn comment_in_tree() {
812+ let mut doc = Document::new();
813+ let root = doc.root();
814+ let comment = doc.create_comment("TODO: add content");
815+ let div = doc.create_element("div");
816+ doc.append_child(root, comment);
817+ doc.append_child(root, div);
818+819+ let children: Vec<NodeId> = doc.children(root).collect();
820+ assert_eq!(children, vec![comment, div]);
821+ assert_eq!(doc.text_content(comment), Some("TODO: add content"));
822+ }
823+}