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::diff::{apply, ApplyError, Change};
38use crate::node::{Mark, Node};
39use serde_json::Value;
40
41/// A mutation transaction over a [`Node`] tree: edits apply in place and are
42/// recorded as a [`Change`] log. Create with [`Node::transform`].
43///
44/// Each builder returns `Result<&mut Self, ApplyError>` so calls chain with `?`;
45/// an error (e.g. a path that doesn't resolve) leaves the tree as mutated by the
46/// changes recorded so far.
47pub struct Transform<'a> {
48    root: &'a mut Node,
49    changes: Vec<Change>,
50}
51
52impl Node {
53    /// Begin a [`Transform`] over this tree.
54    pub fn transform(&mut self) -> Transform<'_> {
55        Transform {
56            root: self,
57            changes: Vec::new(),
58        }
59    }
60}
61
62impl<'a> Transform<'a> {
63    /// Apply `change` in place and record it.
64    fn push(&mut self, change: Change) -> Result<&mut Self, ApplyError> {
65        apply(self.root, std::slice::from_ref(&change))?;
66        self.changes.push(change);
67        Ok(self)
68    }
69
70    /// Set (insert or overwrite) attribute `key` on the node at `path`.
71    pub fn set_attr(
72        &mut self,
73        path: Vec<usize>,
74        key: impl Into<String>,
75        value: impl Into<Value>,
76    ) -> Result<&mut Self, ApplyError> {
77        self.push(Change::SetAttr {
78            path,
79            key: key.into(),
80            value: value.into(),
81        })
82    }
83
84    /// Remove attribute `key` from the node at `path`.
85    pub fn remove_attr(
86        &mut self,
87        path: Vec<usize>,
88        key: impl Into<String>,
89    ) -> Result<&mut Self, ApplyError> {
90        self.push(Change::RemoveAttr {
91            path,
92            key: key.into(),
93        })
94    }
95
96    /// Set the text payload of the node at `path` (`None` clears it).
97    pub fn set_text(
98        &mut self,
99        path: Vec<usize>,
100        text: Option<String>,
101    ) -> Result<&mut Self, ApplyError> {
102        self.push(Change::SetText { path, text })
103    }
104
105    /// Replace the whole mark list of the node at `path` (`None` clears it).
106    pub fn set_marks(
107        &mut self,
108        path: Vec<usize>,
109        marks: Option<Vec<Mark>>,
110    ) -> Result<&mut Self, ApplyError> {
111        self.push(Change::SetMarks { path, marks })
112    }
113
114    /// Set (insert or overwrite) unknown top-level field `key` on the node at `path`.
115    pub fn set_extra(
116        &mut self,
117        path: Vec<usize>,
118        key: impl Into<String>,
119        value: impl Into<Value>,
120    ) -> Result<&mut Self, ApplyError> {
121        self.push(Change::SetExtra {
122            path,
123            key: key.into(),
124            value: value.into(),
125        })
126    }
127
128    /// Remove unknown top-level field `key` from the node at `path`.
129    pub fn remove_extra(
130        &mut self,
131        path: Vec<usize>,
132        key: impl Into<String>,
133    ) -> Result<&mut Self, ApplyError> {
134        self.push(Change::RemoveExtra {
135            path,
136            key: key.into(),
137        })
138    }
139
140    /// Insert `node` as a child of the node at `path` (the parent), at `index`.
141    pub fn insert(
142        &mut self,
143        path: Vec<usize>,
144        index: usize,
145        node: Node,
146    ) -> Result<&mut Self, ApplyError> {
147        self.push(Change::Insert { path, index, node })
148    }
149
150    /// Remove the child at `index` of the node at `path` (the parent).
151    pub fn remove(&mut self, path: Vec<usize>, index: usize) -> Result<&mut Self, ApplyError> {
152        self.push(Change::Remove { path, index })
153    }
154
155    /// Replace the node at `path` wholesale.
156    pub fn replace(&mut self, path: Vec<usize>, node: Node) -> Result<&mut Self, ApplyError> {
157        self.push(Change::Replace { path, node })
158    }
159
160    /// Relocate the child at `from` to `to` within the parent at `path`, without
161    /// cloning its subtree. See [`Change::Move`].
162    pub fn move_child(
163        &mut self,
164        path: Vec<usize>,
165        from: usize,
166        to: usize,
167    ) -> Result<&mut Self, ApplyError> {
168        self.push(Change::Move { path, from, to })
169    }
170
171    /// The changes recorded so far, without consuming the transaction.
172    pub fn changes(&self) -> &[Change] {
173        &self.changes
174    }
175
176    /// Finish the transaction, returning the recorded [`Change`] log.
177    pub fn finish(self) -> Vec<Change> {
178        self.changes
179    }
180}