tiptap_rusty_parser/path.rs
1//! Positional addressing by index path.
2//!
3//! A *path* is a slice of child indices from a node, root = `&[]`. So in a
4//! `doc -> paragraph -> text` tree, the text node is at `&[0, 0]`.
5//!
6//! There are no stored parent pointers; parent/sibling navigation is expressed
7//! by slicing a path: the parent of `path` is `node.node_at(&path[..path.len()-1])`.
8
9use crate::node::Node;
10
11impl Node {
12 /// Node at `path` (relative to `self`), or `None` if any index is missing.
13 ///
14 /// ```
15 /// use tiptap_rusty_parser::Document;
16 /// let doc = Document::from_json_str(
17 /// r#"{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hi"}]}]}"#,
18 /// ).unwrap();
19 /// assert_eq!(doc.node_at(&[0, 0]).unwrap().get_text(), Some("hi"));
20 /// assert!(doc.node_at(&[5]).is_none());
21 /// ```
22 pub fn node_at(&self, path: &[usize]) -> Option<&Node> {
23 let mut cur = self;
24 for &i in path {
25 cur = cur.content.as_ref()?.get(i)?;
26 }
27 Some(cur)
28 }
29
30 /// Mutable variant of [`Node::node_at`].
31 pub fn node_at_mut(&mut self, path: &[usize]) -> Option<&mut Node> {
32 let mut cur = self;
33 for &i in path {
34 cur = cur.content.as_mut()?.get_mut(i)?;
35 }
36 Some(cur)
37 }
38
39 /// Path to the first node (incl. `self`) matching `pred`, pre-order.
40 ///
41 /// Returns `Some(vec![])` when `self` matches.
42 ///
43 /// ```
44 /// use tiptap_rusty_parser::Document;
45 /// let doc = Document::from_json_str(
46 /// r#"{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hi"}]}]}"#,
47 /// ).unwrap();
48 /// let p = doc.path_to(|n| n.node_type.as_deref() == Some("text")).unwrap();
49 /// assert_eq!(p, vec![0, 0]);
50 /// assert_eq!(doc.node_at(&p).unwrap().get_text(), Some("hi"));
51 /// ```
52 pub fn path_to(&self, mut pred: impl FnMut(&Node) -> bool) -> Option<Vec<usize>> {
53 let mut path = Vec::new();
54 if path_to_rec(self, &mut pred, &mut path) {
55 Some(path)
56 } else {
57 None
58 }
59 }
60
61 /// Paths to every node (incl. `self`) matching `pred`, pre-order.
62 pub fn paths_to(&self, mut pred: impl FnMut(&Node) -> bool) -> Vec<Vec<usize>> {
63 let mut out = Vec::new();
64 let mut path = Vec::new();
65 paths_to_rec(self, &mut pred, &mut path, &mut out);
66 out
67 }
68}
69
70fn path_to_rec(node: &Node, pred: &mut impl FnMut(&Node) -> bool, path: &mut Vec<usize>) -> bool {
71 if pred(node) {
72 return true;
73 }
74 if let Some(children) = &node.content {
75 for (i, child) in children.iter().enumerate() {
76 path.push(i);
77 if path_to_rec(child, pred, path) {
78 return true;
79 }
80 path.pop();
81 }
82 }
83 false
84}
85
86fn paths_to_rec(
87 node: &Node,
88 pred: &mut impl FnMut(&Node) -> bool,
89 path: &mut Vec<usize>,
90 out: &mut Vec<Vec<usize>>,
91) {
92 if pred(node) {
93 out.push(path.clone());
94 }
95 if let Some(children) = &node.content {
96 for (i, child) in children.iter().enumerate() {
97 path.push(i);
98 paths_to_rec(child, pred, path, out);
99 path.pop();
100 }
101 }
102}