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