1use crate::bitflags_like;
7use crate::dom::Dom;
8use crate::node_id::NodeId;
9
10bitflags_like! {
11 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16 pub struct DocumentPosition(u16) {
17 DISCONNECTED = 0b0000_0001;
18 PRECEDING = 0b0000_0010;
19 FOLLOWING = 0b0000_0100;
20 CONTAINS = 0b0000_1000;
21 CONTAINED_BY = 0b0001_0000;
22 IMPLEMENTATION_SPECIFIC = 0b0010_0000;
23 }
24}
25
26impl<Ext> Dom<Ext> {
27 pub fn compare_document_position(&self, a: NodeId, b: NodeId) -> DocumentPosition {
41 if a == b {
42 return DocumentPosition::empty();
43 }
44 const IN_DIFFERENT_TREES: DocumentPosition = DocumentPosition::from_bits_truncate(
46 DocumentPosition::DISCONNECTED.bits()
47 | DocumentPosition::IMPLEMENTATION_SPECIFIC.bits()
48 | DocumentPosition::PRECEDING.bits(),
49 );
50 let (Some(da), Some(db)) = (self.depth_of(a), self.depth_of(b)) else {
51 return IN_DIFFERENT_TREES;
52 };
53 let (mut x, mut y) = (a, b);
58 for _ in db..da {
59 x = self.parent_of(x);
60 }
61 for _ in da..db {
62 y = self.parent_of(y);
63 }
64 if x == y {
65 return if da < db {
66 DocumentPosition::CONTAINED_BY | DocumentPosition::FOLLOWING
69 } else {
70 DocumentPosition::CONTAINS | DocumentPosition::PRECEDING
72 };
73 }
74 let lca = loop {
77 match (
78 self.get_node(x).and_then(|n| n.parent),
79 self.get_node(y).and_then(|n| n.parent),
80 ) {
81 (Some(px), Some(py)) if px == py => break px,
82 (Some(px), Some(py)) => {
83 x = px;
84 y = py;
85 }
86 _ => return IN_DIFFERENT_TREES,
88 }
89 };
90 let mut cur = self.get_node(lca).and_then(|n| n.first_child);
93 while let Some(c) = cur {
94 if c == x {
95 return DocumentPosition::FOLLOWING;
96 }
97 if c == y {
98 return DocumentPosition::PRECEDING;
99 }
100 cur = self.get_node(c).and_then(|n| n.next_sibling);
101 }
102 debug_assert!(
103 false,
104 "compare_document_position: branches not under their LCA"
105 );
106 DocumentPosition::empty()
107 }
108
109 fn depth_of(&self, id: NodeId) -> Option<usize> {
112 let mut node = self.get_node(id)?;
113 let mut depth = 0;
114 while let Some(p) = node.parent {
115 node = self.get_node(p)?;
116 depth += 1;
117 }
118 Some(depth)
119 }
120
121 fn parent_of(&self, id: NodeId) -> NodeId {
124 self.get_node(id)
125 .and_then(|n| n.parent)
126 .expect("parent_of: node has a parent (depth checked by caller)")
127 }
128
129 pub(crate) fn root_of(&self, id: NodeId) -> Option<NodeId> {
131 let mut cur = id;
132 let mut node = self.get_node(cur)?;
133 while let Some(p) = node.parent {
134 cur = p;
135 node = self.get_node(cur)?;
136 }
137 Some(cur)
138 }
139
140 pub fn is_equal_node(&self, a: NodeId, b: NodeId) -> bool {
145 let mut pending = vec![(a, b)];
146 while let Some((a, b)) = pending.pop() {
147 let (Some(na), Some(nb)) = (self.get_node(a), self.get_node(b)) else {
148 return false;
149 };
150 if !Self::same_node_data(&na.data, &nb.data) {
151 return false;
152 }
153 let (mut ca, mut cb) = (na.first_child, nb.first_child);
155 loop {
156 match (ca, cb) {
157 (None, None) => break,
158 (Some(x), Some(y)) => {
159 pending.push((x, y));
160 ca = self.get_node(x).and_then(|n| n.next_sibling);
161 cb = self.get_node(y).and_then(|n| n.next_sibling);
162 }
163 _ => return false,
164 }
165 }
166 }
167 true
168 }
169
170 fn same_node_data(a: &crate::node::NodeData<Ext>, b: &crate::node::NodeData<Ext>) -> bool {
173 use crate::node::NodeData;
174 match (a, b) {
175 (
176 NodeData::Element {
177 tag: ta,
178 attrs: aa,
179 classes: ca,
180 ..
181 },
182 NodeData::Element {
183 tag: tb,
184 attrs: ab,
185 classes: cb,
186 ..
187 },
188 ) => ta == tb && aa == ab && ca == cb,
189 (NodeData::Text { data: da }, NodeData::Text { data: db })
190 | (NodeData::Comment { data: da }, NodeData::Comment { data: db }) => da == db,
191 (NodeData::Fragment, NodeData::Fragment) => true,
192 _ => false,
193 }
194 }
195
196 pub fn compare_boundary_points(
205 &self,
206 a: crate::Position,
207 b: crate::Position,
208 ) -> Option<std::cmp::Ordering> {
209 use std::cmp::Ordering;
210 if a.node == b.node {
211 return Some(a.offset.cmp(&b.offset));
212 }
213 let pos = self.compare_document_position(a.node, b.node);
214 if pos.contains(DocumentPosition::DISCONNECTED) {
215 return None;
216 }
217 if pos.contains(DocumentPosition::PRECEDING) {
220 return self.compare_boundary_points(b, a).map(Ordering::reverse);
221 }
222 if pos.contains(DocumentPosition::CONTAINED_BY) {
225 let mut child = b.node;
227 loop {
228 let parent = self.get_node(child)?.parent?;
229 if parent == a.node {
230 break;
231 }
232 child = parent;
233 }
234 let index = self.child_index_of(child)?;
235 return Some(if index < a.offset {
236 Ordering::Greater
237 } else {
238 Ordering::Less
239 });
240 }
241 Some(Ordering::Less)
243 }
244
245 fn child_index_of(&self, id: NodeId) -> Option<usize> {
248 let parent = self.get_node(id)?.parent?;
249 let mut cur = self.get_node(parent)?.first_child;
250 let mut index = 0;
251 while let Some(c) = cur {
252 if c == id {
253 return Some(index);
254 }
255 index += 1;
256 cur = self.get_node(c)?.next_sibling;
257 }
258 None
259 }
260
261 pub fn ancestor_path(&self, id: NodeId) -> Vec<NodeId> {
264 let mut path = Vec::new();
265 let mut cur = Some(id);
266 while let Some(c) = cur {
267 if self.get_node(c).is_none() {
268 return Vec::new();
269 }
270 path.push(c);
271 cur = self.get_node(c).and_then(|n| n.parent);
272 }
273 path.reverse();
274 path
275 }
276
277 pub fn common_ancestor(&self, a: NodeId, b: NodeId) -> Option<NodeId> {
288 let (da, db) = (self.depth_of(a)?, self.depth_of(b)?);
289 let (mut x, mut y) = (a, b);
290 for _ in db..da {
291 x = self.parent_of(x);
292 }
293 for _ in da..db {
294 y = self.parent_of(y);
295 }
296 while x != y {
297 x = self.get_node(x)?.parent?;
300 y = self.get_node(y)?.parent?;
301 }
302 Some(x)
303 }
304}
305
306#[macro_export]
314#[doc(hidden)]
315macro_rules! bitflags_like {
316 (
317 $(#[$outer:meta])*
318 $vis:vis struct $name:ident ( $repr:ty ) {
319 $( $flag:ident = $value:expr; )+
320 }
321 ) => {
322 $(#[$outer])*
323 $vis struct $name($repr);
324
325 impl $name {
326 $( pub const $flag: Self = Self($value); )+
327
328 #[inline] pub const fn empty() -> Self { Self(0) }
329 #[inline] pub const fn all() -> Self { Self( $( $value )|+ ) }
330 #[inline] pub const fn bits(self) -> $repr { self.0 }
331 #[inline] pub const fn from_bits_truncate(bits: $repr) -> Self {
332 Self(bits & Self::all().0)
333 }
334 #[inline] pub const fn contains(self, other: Self) -> bool {
335 (self.0 & other.0) == other.0
336 }
337 #[inline] pub const fn is_empty(self) -> bool { self.0 == 0 }
338 #[inline] pub const fn without(self, other: Self) -> Self {
341 Self(self.0 & !other.0)
342 }
343 }
344
345 impl std::ops::BitOr for $name {
346 type Output = Self;
347 #[inline] fn bitor(self, rhs: Self) -> Self { Self(self.0 | rhs.0) }
348 }
349 impl std::ops::BitOrAssign for $name {
350 #[inline] fn bitor_assign(&mut self, rhs: Self) { self.0 |= rhs.0; }
351 }
352 impl std::ops::BitAnd for $name {
353 type Output = Self;
354 #[inline] fn bitand(self, rhs: Self) -> Self { Self(self.0 & rhs.0) }
355 }
356 impl std::ops::BitAndAssign for $name {
357 #[inline] fn bitand_assign(&mut self, rhs: Self) { self.0 &= rhs.0; }
358 }
359 }
360}
361
362#[cfg(test)]
363mod tests {
364 use super::*;
365 use crate::Dom;
366
367 fn reference_compare(dom: &Dom, a: NodeId, b: NodeId) -> DocumentPosition {
370 if a == b {
371 return DocumentPosition::empty();
372 }
373 let a_path = dom.ancestor_path(a);
374 let b_path = dom.ancestor_path(b);
375 if a_path.first() != b_path.first() || a_path.is_empty() {
376 return DocumentPosition::DISCONNECTED
377 | DocumentPosition::IMPLEMENTATION_SPECIFIC
378 | DocumentPosition::PRECEDING;
379 }
380 let mut common = 0;
381 while common < a_path.len() && common < b_path.len() && a_path[common] == b_path[common] {
382 common += 1;
383 }
384 if common == a_path.len() {
385 return DocumentPosition::CONTAINED_BY | DocumentPosition::FOLLOWING;
386 }
387 if common == b_path.len() {
388 return DocumentPosition::CONTAINS | DocumentPosition::PRECEDING;
389 }
390 let lca = a_path[common - 1];
391 let mut cur = dom.get_node(lca).and_then(|n| n.first_child);
392 while let Some(c) = cur {
393 if c == a_path[common] {
394 return DocumentPosition::FOLLOWING;
395 }
396 if c == b_path[common] {
397 return DocumentPosition::PRECEDING;
398 }
399 cur = dom.get_node(c).and_then(|n| n.next_sibling);
400 }
401 unreachable!()
402 }
403
404 #[test]
407 fn depth_walk_matches_the_path_oracle_on_every_pair() {
408 let mut dom: Dom = Dom::new();
409 let root = dom.root();
410 let mut all = vec![root];
411 let mut frontier = vec![root];
413 for _ in 0..3 {
414 let mut next = Vec::new();
415 for &p in &frontier {
416 for _ in 0..3 {
417 let c = dom.create_element("n");
418 dom.append_child(p, c).unwrap();
419 all.push(c);
420 next.push(c);
421 }
422 }
423 frontier = next;
424 }
425 let loose = dom.create_element("loose");
427 let loose_child = dom.create_element("lc");
428 dom.append_child(loose, loose_child).unwrap();
429 all.push(loose);
430 all.push(loose_child);
431 let gone = dom.create_element("gone");
433 dom.drop_subtree(gone).unwrap();
434 all.push(gone);
435
436 for &a in &all {
437 for &b in &all {
438 assert_eq!(
439 dom.compare_document_position(a, b),
440 reference_compare(&dom, a, b),
441 "pair ({a:?}, {b:?})"
442 );
443 let expected_lca = {
444 let pa = dom.ancestor_path(a);
445 let pb = dom.ancestor_path(b);
446 pa.iter()
447 .zip(pb.iter())
448 .take_while(|(x, y)| x == y)
449 .last()
450 .map(|(x, _)| *x)
451 };
452 assert_eq!(
453 dom.common_ancestor(a, b),
454 expected_lca,
455 "lca ({a:?}, {b:?})"
456 );
457 }
458 }
459 }
460
461 fn build() -> (Dom, NodeId, NodeId, NodeId, NodeId) {
462 let mut dom: Dom = Dom::new();
467 let root = dom.root();
468 let a = dom.create_element("a");
469 let b = dom.create_element("b");
470 let grandchild = dom.create_element("g");
471 dom.append_child(root, a).unwrap();
472 dom.append_child(root, b).unwrap();
473 dom.append_child(a, grandchild).unwrap();
474 (dom, a, b, grandchild, root)
475 }
476
477 #[test]
478 fn self_is_empty() {
479 let (dom, a, _, _, _) = build();
480 assert_eq!(
481 dom.compare_document_position(a, a),
482 DocumentPosition::empty()
483 );
484 }
485
486 #[test]
490 fn descendant_argument_is_contained_by_and_following() {
491 let (dom, a, _, g, _) = build();
492 let r = dom.compare_document_position(a, g);
493 assert_eq!(
494 r,
495 DocumentPosition::CONTAINED_BY | DocumentPosition::FOLLOWING
496 );
497 }
498
499 #[test]
501 fn ancestor_argument_contains_and_precedes() {
502 let (dom, a, _, g, _) = build();
503 let r = dom.compare_document_position(g, a);
504 assert_eq!(r, DocumentPosition::CONTAINS | DocumentPosition::PRECEDING);
505 }
506
507 #[test]
510 fn following_bit_is_consistent_across_containment_and_siblings() {
511 let (dom, a, b, g, _) = build();
512 assert!(
514 dom.compare_document_position(a, g)
515 .contains(DocumentPosition::FOLLOWING)
516 );
517 assert!(
518 dom.compare_document_position(a, b)
519 .contains(DocumentPosition::FOLLOWING)
520 );
521 assert!(
522 dom.compare_document_position(g, b)
523 .contains(DocumentPosition::FOLLOWING)
524 );
525 }
526
527 #[test]
530 fn boundary_points_same_node_order_by_offset() {
531 use crate::Position;
532 use std::cmp::Ordering;
533 let (dom, a, _, _, _) = build();
534 assert_eq!(
535 dom.compare_boundary_points(Position::new(a, 0), Position::new(a, 1)),
536 Some(Ordering::Less)
537 );
538 assert_eq!(
539 dom.compare_boundary_points(Position::new(a, 1), Position::new(a, 1)),
540 Some(Ordering::Equal)
541 );
542 }
543
544 #[test]
547 fn boundary_points_ancestor_offset_splits_around_child_index() {
548 use crate::Position;
549 use std::cmp::Ordering;
550 let (dom, a, _, g, root) = build();
551 let in_g = Position::new(g, 0);
553 assert_eq!(
554 dom.compare_boundary_points(Position::new(root, 0), in_g),
555 Some(Ordering::Less)
556 );
557 assert_eq!(
558 dom.compare_boundary_points(Position::new(root, 1), in_g),
559 Some(Ordering::Greater)
560 );
561 assert_eq!(
563 dom.compare_boundary_points(in_g, Position::new(root, 1)),
564 Some(Ordering::Less)
565 );
566 assert_eq!(
567 dom.compare_boundary_points(in_g, Position::new(a, 0)),
568 Some(Ordering::Greater)
569 );
570 }
571
572 #[test]
575 fn boundary_point_offset_beyond_child_count_orders_after_all_children() {
576 use crate::Position;
577 use std::cmp::Ordering;
578 let (dom, a, b, g, root) = build();
579 let end = Position::new(root, 99);
580 for inside in [
581 Position::new(a, 0),
582 Position::new(g, 0),
583 Position::new(b, 0),
584 ] {
585 assert_eq!(
586 dom.compare_boundary_points(end, inside),
587 Some(Ordering::Greater)
588 );
589 assert_eq!(
590 dom.compare_boundary_points(inside, end),
591 Some(Ordering::Less)
592 );
593 }
594 }
595
596 #[test]
597 fn boundary_points_disconnected_is_none() {
598 use crate::Position;
599 let (mut dom, a, _, _, _) = build();
600 let loose = dom.create_element("x");
601 assert_eq!(
602 dom.compare_boundary_points(Position::new(a, 0), Position::new(loose, 0)),
603 None
604 );
605 }
606
607 #[test]
608 fn siblings_ordered_by_position() {
609 let (dom, a, b, _, _) = build();
610 assert!(
611 dom.compare_document_position(a, b)
612 .contains(DocumentPosition::FOLLOWING)
613 );
614 assert!(
615 dom.compare_document_position(b, a)
616 .contains(DocumentPosition::PRECEDING)
617 );
618 }
619
620 #[test]
621 fn disconnected_nodes_flagged() {
622 let mut dom: Dom = Dom::new();
623 let a = dom.create_element("a"); let b = dom.create_element("b"); let r = dom.compare_document_position(a, b);
626 assert!(r.contains(DocumentPosition::DISCONNECTED));
627 }
628
629 #[test]
632 fn common_ancestor_self_is_self() {
633 let (dom, a, _, _, _) = build();
634 assert_eq!(dom.common_ancestor(a, a), Some(a));
635 }
636
637 #[test]
638 fn common_ancestor_siblings_is_parent() {
639 let (dom, a, b, _, root) = build();
640 assert_eq!(dom.common_ancestor(a, b), Some(root));
641 }
642
643 #[test]
644 fn common_ancestor_nested_is_ancestor() {
645 let (dom, a, _, g, _) = build();
647 assert_eq!(dom.common_ancestor(a, g), Some(a));
648 assert_eq!(dom.common_ancestor(g, a), Some(a));
649 }
650
651 #[test]
652 fn common_ancestor_cousins_is_lca() {
653 let (dom, _, b, g, root) = build();
655 assert_eq!(dom.common_ancestor(g, b), Some(root));
656 }
657
658 #[test]
659 fn common_ancestor_disconnected_returns_none() {
660 let mut dom: Dom = Dom::new();
661 let a = dom.create_element("a"); let b = dom.create_element("b"); assert_eq!(dom.common_ancestor(a, b), None);
664 }
665
666 #[test]
669 fn equal_node_same_tag_and_attrs() {
670 let mut dom: Dom = Dom::new();
671 let a = dom.create_element("div");
672 let b = dom.create_element("div");
673 dom.set_attribute(a, "role", "banner").unwrap();
674 dom.set_attribute(b, "role", "banner").unwrap();
675 assert!(dom.is_equal_node(a, b));
676 }
677
678 #[test]
679 fn unequal_different_tag() {
680 let mut dom: Dom = Dom::new();
681 let a = dom.create_element("div");
682 let b = dom.create_element("span");
683 assert!(!dom.is_equal_node(a, b));
684 }
685
686 #[test]
687 fn unequal_different_attr() {
688 let mut dom: Dom = Dom::new();
689 let a = dom.create_element("div");
690 let b = dom.create_element("div");
691 dom.set_attribute(a, "role", "banner").unwrap();
692 dom.set_attribute(b, "role", "navigation").unwrap();
693 assert!(!dom.is_equal_node(a, b));
694 }
695
696 #[test]
697 fn equal_text_nodes_same_data() {
698 let mut dom: Dom = Dom::new();
699 let a = dom.create_text_node("hi");
700 let b = dom.create_text_node("hi");
701 let c = dom.create_text_node("bye");
702 assert!(dom.is_equal_node(a, b));
703 assert!(!dom.is_equal_node(a, c));
704 }
705
706 #[test]
707 fn equal_with_children() {
708 let mut dom: Dom = Dom::new();
709 let a = dom.create_element("div");
710 let a1 = dom.create_text_node("hello");
711 dom.append_child(a, a1).unwrap();
712
713 let b = dom.create_element("div");
714 let b1 = dom.create_text_node("hello");
715 dom.append_child(b, b1).unwrap();
716
717 assert!(dom.is_equal_node(a, b));
718 }
719
720 #[test]
721 fn unequal_different_child_count() {
722 let mut dom: Dom = Dom::new();
723 let a = dom.create_element("div");
724 let a1 = dom.create_text_node("x");
725 dom.append_child(a, a1).unwrap();
726
727 let b = dom.create_element("div");
728 assert!(!dom.is_equal_node(a, b));
731 }
732
733 #[test]
734 fn unequal_different_node_types() {
735 let mut dom: Dom = Dom::new();
736 let a = dom.create_element("div");
737 let b = dom.create_text_node("div");
738 assert!(!dom.is_equal_node(a, b));
739 }
740}