Skip to main content

tiptap_rusty_parser/
transform.rs

1//! Transactions: mutate a [`Node`] tree while recording a replayable,
2//! invertible [`Change`] log in the same pass.
3//!
4//! A [`Transform`] borrows the tree mutably; each builder method both applies an
5//! edit in place (via the same engine as [`apply`](crate::apply)) **and** records
6//! the corresponding [`Change`]. Calling [`finish`](Transform::finish) returns
7//! the recorded list, which — applied to a clone of the *original* tree —
8//! reproduces the transformed tree, and whose [`invert`](crate::invert) is its
9//! undo. This unifies the mutation API with [`diff`](crate::diff): instead of
10//! editing and then diffing to recover a patch, you get the patch for free.
11//!
12//! ```
13//! use tiptap_rusty_parser::{apply, Node};
14//!
15//! let mut doc = Node::element("doc").with_child(Node::element("paragraph"));
16//! let original = doc.clone();
17//!
18//! let changes = {
19//!     let mut tx = doc.transform();
20//!     tx.set_attr(vec![0], "level", 1).unwrap();
21//!     tx.insert(vec![], 1, Node::element("paragraph")).unwrap();
22//!     tx.finish()
23//! };
24//!
25//! // The recorded log reproduces `doc` from a clone of the original.
26//! let mut replay = original.clone();
27//! apply(&mut replay, &changes).unwrap();
28//! assert_eq!(replay, doc);
29//!
30//! // …and inverts to an undo that restores the original.
31//! let undo = original.invert(&changes).unwrap();
32//! let mut back = doc.clone();
33//! apply(&mut back, &undo).unwrap();
34//! assert_eq!(back, original);
35//! ```
36
37use crate::block::{BlockError, BlockRange};
38use crate::diff::{apply, ApplyError, Change};
39use crate::node::{Mark, Node};
40use crate::range::Position;
41use serde_json::{Map, Value};
42
43/// A mutation transaction over a [`Node`] tree: edits apply in place and are
44/// recorded as a [`Change`] log. Create with [`Node::transform`].
45///
46/// Each builder returns `Result<&mut Self, ApplyError>` so calls chain with `?`;
47/// an error (e.g. a path that doesn't resolve) leaves the tree as mutated by the
48/// changes recorded so far.
49pub struct Transform<'a> {
50    root: &'a mut Node,
51    changes: Vec<Change>,
52}
53
54impl Node {
55    /// Begin a [`Transform`] over this tree.
56    pub fn transform(&mut self) -> Transform<'_> {
57        Transform {
58            root: self,
59            changes: Vec::new(),
60        }
61    }
62}
63
64impl<'a> Transform<'a> {
65    /// Apply `change` in place and record it.
66    fn push(&mut self, change: Change) -> Result<&mut Self, ApplyError> {
67        apply(self.root, std::slice::from_ref(&change))?;
68        self.changes.push(change);
69        Ok(self)
70    }
71
72    /// Set (insert or overwrite) attribute `key` on the node at `path`.
73    pub fn set_attr(
74        &mut self,
75        path: Vec<usize>,
76        key: impl Into<String>,
77        value: impl Into<Value>,
78    ) -> Result<&mut Self, ApplyError> {
79        self.push(Change::SetAttr {
80            path,
81            key: key.into(),
82            value: value.into(),
83        })
84    }
85
86    /// Remove attribute `key` from the node at `path`.
87    pub fn remove_attr(
88        &mut self,
89        path: Vec<usize>,
90        key: impl Into<String>,
91    ) -> Result<&mut Self, ApplyError> {
92        self.push(Change::RemoveAttr {
93            path,
94            key: key.into(),
95        })
96    }
97
98    /// Set the text payload of the node at `path` (`None` clears it).
99    pub fn set_text(
100        &mut self,
101        path: Vec<usize>,
102        text: Option<String>,
103    ) -> Result<&mut Self, ApplyError> {
104        self.push(Change::SetText { path, text })
105    }
106
107    /// Replace the whole mark list of the node at `path` (`None` clears it).
108    pub fn set_marks(
109        &mut self,
110        path: Vec<usize>,
111        marks: Option<Vec<Mark>>,
112    ) -> Result<&mut Self, ApplyError> {
113        self.push(Change::SetMarks { path, marks })
114    }
115
116    /// Set (insert or overwrite) unknown top-level field `key` on the node at `path`.
117    pub fn set_extra(
118        &mut self,
119        path: Vec<usize>,
120        key: impl Into<String>,
121        value: impl Into<Value>,
122    ) -> Result<&mut Self, ApplyError> {
123        self.push(Change::SetExtra {
124            path,
125            key: key.into(),
126            value: value.into(),
127        })
128    }
129
130    /// Remove unknown top-level field `key` from the node at `path`.
131    pub fn remove_extra(
132        &mut self,
133        path: Vec<usize>,
134        key: impl Into<String>,
135    ) -> Result<&mut Self, ApplyError> {
136        self.push(Change::RemoveExtra {
137            path,
138            key: key.into(),
139        })
140    }
141
142    /// Insert `node` as a child of the node at `path` (the parent), at `index`.
143    pub fn insert(
144        &mut self,
145        path: Vec<usize>,
146        index: usize,
147        node: Node,
148    ) -> Result<&mut Self, ApplyError> {
149        self.push(Change::Insert { path, index, node })
150    }
151
152    /// Remove the child at `index` of the node at `path` (the parent).
153    pub fn remove(&mut self, path: Vec<usize>, index: usize) -> Result<&mut Self, ApplyError> {
154        self.push(Change::Remove { path, index })
155    }
156
157    /// Replace the node at `path` wholesale.
158    pub fn replace(&mut self, path: Vec<usize>, node: Node) -> Result<&mut Self, ApplyError> {
159        self.push(Change::Replace { path, node })
160    }
161
162    /// Relocate the child at `from` to `to` within the parent at `path`, without
163    /// cloning its subtree. See [`Change::Move`].
164    pub fn move_child(
165        &mut self,
166        path: Vec<usize>,
167        from: usize,
168        to: usize,
169    ) -> Result<&mut Self, ApplyError> {
170        self.push(Change::Move { path, from, to })
171    }
172
173    /// The changes recorded so far, without consuming the transaction.
174    pub fn changes(&self) -> &[Change] {
175        &self.changes
176    }
177
178    /// Finish the transaction, returning the recorded [`Change`] log.
179    pub fn finish(self) -> Vec<Change> {
180        self.changes
181    }
182}
183
184/// Block-structural builders. Unlike the field/child ops above (which map to a
185/// single [`Change`]), these restructure the tree and are recorded by running
186/// the in-place edit and recovering the patch via [`diff`](crate::diff) — so
187/// they clone the tree once (only on this transaction path; the direct
188/// [`Node`] methods stay clone-free).
189impl Transform<'_> {
190    fn record_structural<F>(&mut self, f: F) -> Result<&mut Self, BlockError>
191    where
192        F: FnOnce(&mut Node) -> Result<(), BlockError>,
193    {
194        let before = self.root.clone();
195        f(self.root)?;
196        let patch = before.diff(self.root);
197        self.changes.extend(patch);
198        Ok(self)
199    }
200
201    /// Record [`Node::set_block_type`].
202    pub fn set_block_type(
203        &mut self,
204        path: Vec<usize>,
205        new_type: impl Into<String>,
206        attrs: Option<Map<String, Value>>,
207    ) -> Result<&mut Self, BlockError> {
208        self.record_structural(|root| root.set_block_type(&path, new_type, attrs))
209    }
210
211    /// Record [`Node::split_block`].
212    pub fn split_block(
213        &mut self,
214        path: Vec<usize>,
215        at: usize,
216        depth: usize,
217    ) -> Result<&mut Self, BlockError> {
218        self.record_structural(|root| root.split_block(&path, at, depth))
219    }
220
221    /// Record [`Node::split_block_at`].
222    pub fn split_block_at(
223        &mut self,
224        path: Vec<usize>,
225        pos: Position,
226        depth: usize,
227    ) -> Result<&mut Self, BlockError> {
228        self.record_structural(|root| root.split_block_at(&path, pos, depth))
229    }
230
231    /// Record [`Node::join_blocks`].
232    pub fn join_blocks(
233        &mut self,
234        parent: Vec<usize>,
235        index: usize,
236    ) -> Result<&mut Self, BlockError> {
237        self.record_structural(|root| root.join_blocks(&parent, index))
238    }
239
240    /// Record [`Node::wrap`].
241    pub fn wrap(
242        &mut self,
243        path: Vec<usize>,
244        wrapper_type: impl Into<String>,
245        attrs: Option<Map<String, Value>>,
246    ) -> Result<&mut Self, BlockError> {
247        self.record_structural(|root| root.wrap(&path, wrapper_type, attrs))
248    }
249
250    /// Record [`Node::wrap_range`].
251    pub fn wrap_range(
252        &mut self,
253        range: BlockRange,
254        wrapper_type: impl Into<String>,
255        attrs: Option<Map<String, Value>>,
256    ) -> Result<&mut Self, BlockError> {
257        self.record_structural(|root| root.wrap_range(&range, wrapper_type, attrs))
258    }
259
260    /// Record [`Node::lift`].
261    pub fn lift(&mut self, path: Vec<usize>) -> Result<&mut Self, BlockError> {
262        self.record_structural(|root| root.lift(&path))
263    }
264}