1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
use std::sync::Arc;
use loro::{ContainerTrait, LoroError, LoroResult, LoroTreeError, TreeID, ID};
use crate::{ContainerID, DiffEvent, LoroDoc, LoroValue, Subscriber, Subscription};
use super::LoroMap;
pub enum TreeParentId {
Node { id: TreeID },
Root,
Deleted,
Unexist,
}
#[derive(Debug, Clone)]
pub struct LoroTree {
pub(crate) inner: loro::LoroTree,
}
impl LoroTree {
pub fn new() -> Self {
Self {
inner: loro::LoroTree::new(),
}
}
/// Whether the container is attached to a document
///
/// The edits on a detached container will not be persisted.
/// To attach the container to the document, please insert it into an attached container.
pub fn is_attached(&self) -> bool {
self.inner.is_attached()
}
/// If a detached container is attached, this method will return its corresponding attached handler.
pub fn get_attached(&self) -> Option<Arc<LoroTree>> {
self.inner
.get_attached()
.map(|x| Arc::new(LoroTree { inner: x }))
}
/// Create a new tree node and return the [`TreeID`].
///
/// If the `parent` is `None`, the created node is the root of a tree.
/// Otherwise, the created node is a child of the parent tree node.
///
/// # Example
///
/// ```rust
/// use loro::LoroDoc;
///
/// let doc = LoroDoc::new();
/// let tree = doc.get_tree("tree");
/// // create a root
/// let root = tree.create(None).unwrap();
/// // create a new child
/// let child = tree.create(root).unwrap();
/// ```
pub fn create(&self, parent: TreeParentId) -> LoroResult<TreeID> {
self.inner.create(parent)
}
/// Create a new tree node at the given index and return the [`TreeID`].
///
/// If the `parent` is `None`, the created node is the root of a tree.
/// If the `index` is greater than the number of children of the parent, error will be returned.
pub fn create_at(&self, parent: TreeParentId, index: u32) -> LoroResult<TreeID> {
self.inner.create_at(parent, index as usize)
}
pub fn roots(&self) -> Vec<TreeID> {
self.inner.roots()
}
/// Move the `target` node to be a child of the `parent` node.
///
/// If the `parent` is `None`, the `target` node will be a root.
///
/// # Example
///
/// ```rust
/// use loro::LoroDoc;
///
/// let doc = LoroDoc::new();
/// let tree = doc.get_tree("tree");
/// let root = tree.create(None).unwrap();
/// let root2 = tree.create(None).unwrap();
/// // move `root2` to be a child of `root`.
/// tree.mov(root2, root).unwrap();
/// ```
pub fn mov(&self, target: TreeID, parent: TreeParentId) -> LoroResult<()> {
self.inner.mov(target, parent)
}
/// Move the `target` node to be a child of the `parent` node at the given index.
/// If the `parent` is `None`, the `target` node will be a root.
pub fn mov_to(&self, target: TreeID, parent: TreeParentId, to: u32) -> LoroResult<()> {
self.inner.mov_to(target, parent, to as usize)
}
/// Move the `target` node to be a child after the `after` node with the same parent.
pub fn mov_after(&self, target: TreeID, after: TreeID) -> LoroResult<()> {
self.inner.mov_after(target, after)
}
/// Move the `target` node to be a child before the `before` node with the same parent.
pub fn mov_before(&self, target: TreeID, before: TreeID) -> LoroResult<()> {
self.inner.mov_before(target, before)
}
/// Delete a tree node.
///
/// Note: If the deleted node has children, the children do not appear in the state
/// rather than actually being deleted.
///
/// # Example
///
/// ```rust
/// use loro::LoroDoc;
///
/// let doc = LoroDoc::new();
/// let tree = doc.get_tree("tree");
/// let root = tree.create(None).unwrap();
/// tree.delete(root).unwrap();
/// ```
pub fn delete(&self, target: TreeID) -> LoroResult<()> {
self.inner.delete(target)
}
/// Get the associated metadata map handler of a tree node.
///
/// # Example
/// ```rust
/// use loro::LoroDoc;
///
/// let doc = LoroDoc::new();
/// let tree = doc.get_tree("tree");
/// let root = tree.create(None).unwrap();
/// let root_meta = tree.get_meta(root).unwrap();
/// root_meta.insert("color", "red");
/// ```
pub fn get_meta(&self, target: TreeID) -> LoroResult<Arc<LoroMap>> {
self.inner
.get_meta(target)
.map(|h| Arc::new(LoroMap { inner: h }))
}
/// Return the parent of target node.
///
/// - If the target node does not exist, throws Error.
/// - If the target node is a root node, return `None`.
pub fn parent(&self, target: TreeID) -> LoroResult<TreeParentId> {
if let Some(p) = self.inner.parent(target) {
Ok(p.into())
} else {
Err(LoroError::TreeError(LoroTreeError::TreeNodeNotExist(
target,
)))
}
}
/// Return whether target node exists.
pub fn contains(&self, target: TreeID) -> bool {
self.inner.contains(target)
}
/// Return whether target node is deleted.
///
/// # Errors
///
/// - If the target node does not exist, return `LoroTreeError::TreeNodeNotExist`.
pub fn is_node_deleted(&self, target: TreeID) -> LoroResult<bool> {
self.inner.is_node_deleted(&target)
}
/// Return all nodes, including deleted nodes
pub fn nodes(&self) -> Vec<TreeID> {
self.inner.nodes()
}
/// Return all children of the target node.
///
/// If the parent node does not exist, return `None`.
pub fn children(&self, parent: TreeParentId) -> Option<Vec<TreeID>> {
self.inner.children(parent)
}
/// Return the number of children of the target node.
pub fn children_num(&self, parent: TreeParentId) -> Option<u32> {
self.inner.children_num(parent).map(|v| v as u32)
}
/// Return container id of the tree.
pub fn id(&self) -> ContainerID {
self.inner.id().into()
}
/// Return the fractional index of the target node with hex format.
pub fn fractional_index(&self, target: TreeID) -> Option<String> {
self.inner.fractional_index(target)
}
/// Return the flat array of the forest.
///
/// Note: the metadata will be not resolved. So if you don't only care about hierarchy
/// but also the metadata, you should use [TreeHandler::get_value_with_meta()].
pub fn get_value(&self) -> LoroValue {
self.inner.get_value().into()
}
/// Return the flat array of the forest, each node is with metadata.
pub fn get_value_with_meta(&self) -> LoroValue {
self.inner.get_value_with_meta().into()
}
/// Whether the fractional index is enabled.
pub fn is_fractional_index_enabled(&self) -> bool {
self.inner.is_fractional_index_enabled()
}
/// Enable fractional index for Tree Position.
///
/// The jitter is used to avoid conflicts when multiple users are creating the node at the same position.
/// value 0 is default, which means no jitter, any value larger than 0 will enable jitter.
///
/// Generally speaking, jitter will affect the growth rate of document size.
/// [Read more about it](https://www.loro.dev/blog/movable-tree#implementation-and-encoding-size)
#[inline]
pub fn enable_fractional_index(&self, jitter: u8) {
self.inner.enable_fractional_index(jitter);
}
/// Disable the fractional index generation for Tree Position when
/// you don't need the Tree's siblings to be sorted. The fractional index will be always default.
#[inline]
pub fn disable_fractional_index(&self) {
self.inner.disable_fractional_index();
}
pub fn is_deleted(&self) -> bool {
self.inner.is_deleted()
}
pub fn get_last_move_id(&self, target: &TreeID) -> Option<ID> {
self.inner.get_last_move_id(target)
}
pub fn doc(&self) -> Option<Arc<LoroDoc>> {
self.inner.doc().map(|x| Arc::new(LoroDoc { doc: x }))
}
pub fn subscribe(&self, subscriber: Arc<dyn Subscriber>) -> Option<Arc<Subscription>> {
self.inner
.subscribe(Arc::new(move |e| {
subscriber.on_diff(DiffEvent::from(e));
}))
.map(|x| Arc::new(x.into()))
}
}
impl Default for LoroTree {
fn default() -> Self {
Self::new()
}
}
impl From<loro::TreeParentId> for TreeParentId {
fn from(value: loro::TreeParentId) -> Self {
match value {
loro::TreeParentId::Node(id) => TreeParentId::Node { id },
loro::TreeParentId::Root => TreeParentId::Root,
loro::TreeParentId::Deleted => TreeParentId::Deleted,
loro::TreeParentId::Unexist => TreeParentId::Unexist,
}
}
}
impl From<TreeParentId> for loro::TreeParentId {
fn from(value: TreeParentId) -> Self {
match value {
TreeParentId::Node { id } => loro::TreeParentId::Node(id),
TreeParentId::Root => loro::TreeParentId::Root,
TreeParentId::Deleted => loro::TreeParentId::Deleted,
TreeParentId::Unexist => loro::TreeParentId::Unexist,
}
}
}