Skip to main content

tiptap_rusty_parser/
mutate.rs

1//! In-place mutation: marks, attrs, children, text, bulk transforms.
2
3use crate::node::{Mark, Node};
4use serde_json::{Map, Value};
5
6impl Node {
7    // ---- marks ----------------------------------------------------------
8
9    /// True if a mark of `mark_type` is present.
10    pub fn has_mark(&self, mark_type: &str) -> bool {
11        self.marks
12            .as_ref()
13            .is_some_and(|m| m.iter().any(|x| x.mark_type == mark_type))
14    }
15
16    /// Reference to the first mark of `mark_type`.
17    pub fn get_mark(&self, mark_type: &str) -> Option<&Mark> {
18        self.marks
19            .as_ref()?
20            .iter()
21            .find(|x| x.mark_type == mark_type)
22    }
23
24    /// Add `mark` if no mark of that type exists yet. Returns `true` if added.
25    pub fn add_mark(&mut self, mark: Mark) -> bool {
26        if self.has_mark(&mark.mark_type) {
27            return false;
28        }
29        self.marks.get_or_insert_with(Vec::new).push(mark);
30        true
31    }
32
33    /// Remove every mark of `mark_type`. Returns count removed.
34    pub fn remove_mark(&mut self, mark_type: &str) -> usize {
35        let Some(marks) = self.marks.as_mut() else {
36            return 0;
37        };
38        let before = marks.len();
39        marks.retain(|m| m.mark_type != mark_type);
40        let removed = before - marks.len();
41        if marks.is_empty() {
42            self.marks = None;
43        }
44        removed
45    }
46
47    /// Toggle a mark: remove if present, else add. Returns `true` if now present.
48    pub fn toggle_mark(&mut self, mark: Mark) -> bool {
49        if self.has_mark(&mark.mark_type) {
50            self.remove_mark(&mark.mark_type);
51            false
52        } else {
53            self.add_mark(mark);
54            true
55        }
56    }
57
58    /// Set an attr on the (first) mark of `mark_type`. Returns `false` if absent.
59    pub fn set_mark_attr(
60        &mut self,
61        mark_type: &str,
62        key: impl Into<String>,
63        value: impl Into<Value>,
64    ) -> bool {
65        let Some(marks) = self.marks.as_mut() else {
66            return false;
67        };
68        let Some(mark) = marks.iter_mut().find(|m| m.mark_type == mark_type) else {
69            return false;
70        };
71        mark.attrs
72            .get_or_insert_with(Map::new)
73            .insert(key.into(), value.into());
74        true
75    }
76
77    /// Drop all marks.
78    pub fn clear_marks(&mut self) {
79        self.marks = None;
80    }
81
82    // ---- attrs ----------------------------------------------------------
83
84    /// Reference to attr `key`.
85    pub fn attr(&self, key: &str) -> Option<&Value> {
86        self.attrs.as_ref()?.get(key)
87    }
88
89    /// Mutable access to the attrs map, creating it if absent.
90    pub fn attrs_mut(&mut self) -> &mut Map<String, Value> {
91        self.attrs.get_or_insert_with(Map::new)
92    }
93
94    /// Set attr `key`, returning the previous value if any.
95    pub fn set_attr(&mut self, key: impl Into<String>, value: impl Into<Value>) -> Option<Value> {
96        self.attrs_mut().insert(key.into(), value.into())
97    }
98
99    /// Remove attr `key`, returning its value if present.
100    pub fn remove_attr(&mut self, key: &str) -> Option<Value> {
101        let attrs = self.attrs.as_mut()?;
102        let prev = attrs.remove(key);
103        if attrs.is_empty() {
104            self.attrs = None;
105        }
106        prev
107    }
108
109    // ---- children -------------------------------------------------------
110
111    /// Number of direct children.
112    #[inline]
113    pub fn child_count(&self) -> usize {
114        self.content.as_ref().map_or(0, Vec::len)
115    }
116
117    /// Direct children slice (empty if none).
118    #[inline]
119    pub fn children(&self) -> &[Node] {
120        self.content.as_deref().unwrap_or(&[])
121    }
122
123    /// Mutable access to children, creating the vec if absent.
124    #[inline]
125    pub fn children_mut(&mut self) -> &mut Vec<Node> {
126        self.content.get_or_insert_with(Vec::new)
127    }
128
129    /// Direct child at `i`.
130    #[inline]
131    pub fn child(&self, i: usize) -> Option<&Node> {
132        self.content.as_ref()?.get(i)
133    }
134
135    /// Mutable direct child at `i`.
136    #[inline]
137    pub fn child_mut(&mut self, i: usize) -> Option<&mut Node> {
138        self.content.as_mut()?.get_mut(i)
139    }
140
141    /// Append a child.
142    #[inline]
143    pub fn push_child(&mut self, node: Node) {
144        self.children_mut().push(node);
145    }
146
147    /// Insert a child at `i` (clamped to len).
148    pub fn insert_child(&mut self, i: usize, node: Node) {
149        let children = self.children_mut();
150        let i = i.min(children.len());
151        children.insert(i, node);
152    }
153
154    /// Remove and return the child at `i`.
155    pub fn remove_child(&mut self, i: usize) -> Option<Node> {
156        let children = self.content.as_mut()?;
157        if i >= children.len() {
158            return None;
159        }
160        let removed = children.remove(i);
161        if children.is_empty() {
162            self.content = None;
163        }
164        Some(removed)
165    }
166
167    /// Replace the child at `i`, returning the old node.
168    pub fn replace_child(&mut self, i: usize, node: Node) -> Option<Node> {
169        let child = self.content.as_mut()?.get_mut(i)?;
170        Some(std::mem::replace(child, node))
171    }
172
173    /// Remove all children.
174    pub fn clear_children(&mut self) {
175        self.content = None;
176    }
177
178    /// Retain only children matching `pred`.
179    pub fn retain_children(&mut self, mut pred: impl FnMut(&Node) -> bool) {
180        if let Some(children) = self.content.as_mut() {
181            children.retain(|c| pred(c));
182            if children.is_empty() {
183                self.content = None;
184            }
185        }
186    }
187
188    // ---- text -----------------------------------------------------------
189
190    /// Text payload, if any.
191    #[inline]
192    pub fn get_text(&self) -> Option<&str> {
193        self.text.as_deref()
194    }
195
196    /// Set text payload.
197    #[inline]
198    pub fn set_text(&mut self, text: impl Into<String>) {
199        self.text = Some(text.into());
200    }
201
202    // ---- bulk transform -------------------------------------------------
203
204    /// Apply `f` to every node (incl. `self`) matching `pred`. Returns count.
205    pub fn replace_all(
206        &mut self,
207        mut pred: impl FnMut(&Node) -> bool,
208        mut f: impl FnMut(&mut Node),
209    ) -> usize {
210        let mut n = 0;
211        self.walk_mut(&mut |node| {
212            if pred(node) {
213                f(node);
214                n += 1;
215            }
216        });
217        n
218    }
219}