1use std::{
2 borrow::Cow,
3 collections::HashMap,
4 sync::{Arc, Mutex},
5};
6
7use loro::{EventTriggerKind, FractionalIndex, TreeID};
8
9use crate::{
10 convert_trait_to_v_or_container, ContainerID, LoroValue, TreeParentId, ValueOrContainer,
11};
12
13#[uniffi::trait_interface]
14pub trait Subscriber: Sync + Send {
15 fn on_diff(&self, diff: DiffEvent);
16}
17
18pub struct DiffEvent {
19 pub triggered_by: EventTriggerKind,
21 pub origin: String,
23 pub current_target: Option<ContainerID>,
25 pub events: Vec<ContainerDiff>,
27}
28
29impl From<loro::event::DiffEvent<'_>> for DiffEvent {
30 fn from(diff_event: loro::event::DiffEvent) -> Self {
31 Self {
32 triggered_by: diff_event.triggered_by,
33 origin: diff_event.origin.to_string(),
34 current_target: diff_event.current_target.map(|v| v.into()),
35 events: diff_event.events.iter().map(ContainerDiff::from).collect(),
36 }
37 }
38}
39
40pub struct PathItem {
41 pub container: ContainerID,
42 pub index: Index,
43}
44
45pub struct ContainerDiff {
47 pub target: ContainerID,
49 pub path: Vec<PathItem>,
51 pub is_unknown: bool,
53 pub diff: Diff,
55}
56
57#[derive(Debug, Clone)]
58pub enum Index {
59 Key { key: String },
60 Seq { index: u32 },
61 Node { target: TreeID },
62}
63
64pub enum Diff {
65 List { diff: Vec<ListDiffItem> },
67 Text { diff: Vec<TextDelta> },
69 Map { diff: MapDelta },
71 Tree { diff: TreeDiff },
73 Counter { diff: f64 },
75 Unknown,
77}
78
79pub enum TextDelta {
80 Retain {
81 retain: u32,
82 attributes: Option<HashMap<String, LoroValue>>,
83 },
84 Insert {
85 insert: String,
86 attributes: Option<HashMap<String, LoroValue>>,
87 },
88 Delete {
89 delete: u32,
90 },
91}
92
93impl From<TextDelta> for loro::TextDelta {
94 fn from(value: TextDelta) -> Self {
95 match value {
96 TextDelta::Retain { retain, attributes } => loro::TextDelta::Retain {
97 retain: retain as usize,
98 attributes: attributes.as_ref().map(|a| {
99 a.iter()
100 .map(|(k, v)| (k.to_string(), v.clone().into()))
101 .collect()
102 }),
103 },
104 TextDelta::Insert { insert, attributes } => loro::TextDelta::Insert {
105 insert,
106 attributes: attributes.as_ref().map(|a| {
107 a.iter()
108 .map(|(k, v)| (k.to_string(), v.clone().into()))
109 .collect()
110 }),
111 },
112 TextDelta::Delete { delete } => loro::TextDelta::Delete {
113 delete: delete as usize,
114 },
115 }
116 }
117}
118
119impl From<loro::TextDelta> for TextDelta {
120 fn from(value: loro::TextDelta) -> Self {
121 match value {
122 loro::TextDelta::Retain { retain, attributes } => TextDelta::Retain {
123 retain: retain as u32,
124 attributes: attributes.as_ref().map(|a| {
125 a.iter()
126 .map(|(k, v)| (k.to_string(), v.clone().into()))
127 .collect()
128 }),
129 },
130 loro::TextDelta::Insert { insert, attributes } => TextDelta::Insert {
131 insert,
132 attributes: attributes.as_ref().map(|a| {
133 a.iter()
134 .map(|(k, v)| (k.to_string(), v.clone().into()))
135 .collect()
136 }),
137 },
138 loro::TextDelta::Delete { delete } => TextDelta::Delete {
139 delete: delete as u32,
140 },
141 }
142 }
143}
144
145impl From<ListDiffItem> for loro::event::ListDiffItem {
146 fn from(value: ListDiffItem) -> Self {
147 match value {
148 ListDiffItem::Insert { insert, is_move } => loro::event::ListDiffItem::Insert {
149 insert: insert
150 .into_iter()
151 .map(convert_trait_to_v_or_container)
152 .collect(),
153 is_move,
154 },
155 ListDiffItem::Delete { delete } => loro::event::ListDiffItem::Delete {
156 delete: delete as usize,
157 },
158 ListDiffItem::Retain { retain } => loro::event::ListDiffItem::Retain {
159 retain: retain as usize,
160 },
161 }
162 }
163}
164
165impl From<MapDelta> for loro::event::MapDelta<'static> {
166 fn from(value: MapDelta) -> Self {
167 loro::event::MapDelta {
168 updated: value
169 .updated
170 .into_iter()
171 .map(|(k, v)| (Cow::Owned(k), v.map(convert_trait_to_v_or_container)))
172 .collect(),
173 }
174 }
175}
176
177impl From<TreeDiffItem> for loro::TreeDiffItem {
178 fn from(value: TreeDiffItem) -> Self {
179 let target: TreeID = value.target;
180 let action = match value.action {
181 TreeExternalDiff::Create {
182 parent,
183 index,
184 fractional_index,
185 } => loro::TreeExternalDiff::Create {
186 parent: parent.into(),
187 index: index as usize,
188 position: FractionalIndex::from_hex_string(fractional_index),
189 },
190 TreeExternalDiff::Move {
191 parent,
192 index,
193 fractional_index,
194 old_parent,
195 old_index,
196 } => loro::TreeExternalDiff::Move {
197 parent: parent.into(),
198 index: index as usize,
199 position: FractionalIndex::from_hex_string(fractional_index),
200 old_parent: old_parent.into(),
201 old_index: old_index as usize,
202 },
203 TreeExternalDiff::Delete {
204 old_parent,
205 old_index,
206 } => loro::TreeExternalDiff::Delete {
207 old_parent: old_parent.into(),
208 old_index: old_index as usize,
209 },
210 };
211 loro::TreeDiffItem { target, action }
212 }
213}
214
215pub enum ListDiffItem {
216 Insert {
218 insert: Vec<Arc<dyn ValueOrContainer>>,
220 is_move: bool,
222 },
223 Delete {
225 delete: u32,
227 },
228 Retain {
232 retain: u32,
234 },
235}
236
237pub struct MapDelta {
238 pub updated: HashMap<String, Option<Arc<dyn ValueOrContainer>>>,
240}
241
242pub struct TreeDiff {
243 pub diff: Vec<TreeDiffItem>,
244}
245
246pub struct TreeDiffItem {
247 pub target: TreeID,
248 pub action: TreeExternalDiff,
249}
250
251pub enum TreeExternalDiff {
252 Create {
253 parent: TreeParentId,
254 index: u32,
255 fractional_index: String,
256 },
257 Move {
258 parent: TreeParentId,
259 index: u32,
260 fractional_index: String,
261 old_parent: TreeParentId,
262 old_index: u32,
263 },
264 Delete {
265 old_parent: TreeParentId,
266 old_index: u32,
267 },
268}
269
270impl<'a> From<&loro::event::ContainerDiff<'a>> for ContainerDiff {
271 fn from(value: &loro::event::ContainerDiff<'a>) -> Self {
272 Self {
273 target: value.target.into(),
274 path: value
275 .path
276 .iter()
277 .map(|(id, index)| PathItem {
278 container: id.into(),
279 index: index.into(),
280 })
281 .collect(),
282 is_unknown: value.is_unknown,
283 diff: (&value.diff).into(),
284 }
285 }
286}
287
288impl From<&loro::Index> for Index {
289 fn from(value: &loro::Index) -> Self {
290 match value {
291 loro::Index::Key(key) => Index::Key {
292 key: key.to_string(),
293 },
294 loro::Index::Seq(index) => Index::Seq {
295 index: *index as u32,
296 },
297 loro::Index::Node(target) => Index::Node { target: *target },
298 }
299 }
300}
301
302impl From<Index> for loro::Index {
303 fn from(value: Index) -> loro::Index {
304 match value {
305 Index::Key { key } => loro::Index::Key(key.into()),
306 Index::Seq { index } => loro::Index::Seq(index as usize),
307 Index::Node { target } => loro::Index::Node(target),
308 }
309 }
310}
311
312impl From<&loro::event::Diff<'_>> for Diff {
313 fn from(value: &loro::event::Diff) -> Self {
314 match value {
315 loro::event::Diff::List(l) => {
316 let mut ans = Vec::with_capacity(l.len());
317 for item in l.iter() {
318 match item {
319 loro::event::ListDiffItem::Insert { insert, is_move } => {
320 let mut new_insert = Vec::with_capacity(insert.len());
321 for v in insert.iter() {
322 new_insert.push(Arc::new(v.clone()) as Arc<dyn ValueOrContainer>);
323 }
324 ans.push(ListDiffItem::Insert {
325 insert: new_insert,
326 is_move: *is_move,
327 });
328 }
329 loro::event::ListDiffItem::Delete { delete } => {
330 ans.push(ListDiffItem::Delete {
331 delete: *delete as u32,
332 });
333 }
334 loro::event::ListDiffItem::Retain { retain } => {
335 ans.push(ListDiffItem::Retain {
336 retain: *retain as u32,
337 });
338 }
339 }
340 }
341 Diff::List { diff: ans }
342 }
343 loro::event::Diff::Text(t) => Diff::Text {
344 diff: t.iter().map(|i| i.clone().into()).collect(),
345 },
346 loro::event::Diff::Map(m) => {
347 let mut updated = HashMap::new();
348 for (key, value) in m.updated.iter() {
349 updated.insert(
350 key.to_string(),
351 value
352 .as_ref()
353 .map(|v| Arc::new(v.clone()) as Arc<dyn ValueOrContainer>),
354 );
355 }
356
357 Diff::Map {
358 diff: MapDelta { updated },
359 }
360 }
361 loro::event::Diff::Tree(t) => {
362 let mut diff = Vec::new();
363 for item in t.iter() {
364 diff.push(TreeDiffItem {
365 target: item.target,
366 action: match &item.action {
367 loro::TreeExternalDiff::Create {
368 parent,
369 index,
370 position,
371 } => TreeExternalDiff::Create {
372 parent: (*parent).into(),
373 index: *index as u32,
374 fractional_index: position.to_string(),
375 },
376 loro::TreeExternalDiff::Move {
377 parent,
378 index,
379 position,
380 old_parent,
381 old_index,
382 } => TreeExternalDiff::Move {
383 parent: (*parent).into(),
384 index: *index as u32,
385 fractional_index: position.to_string(),
386 old_parent: (*old_parent).into(),
387 old_index: *old_index as u32,
388 },
389 loro::TreeExternalDiff::Delete {
390 old_parent,
391 old_index,
392 } => TreeExternalDiff::Delete {
393 old_parent: (*old_parent).into(),
394 old_index: *old_index as u32,
395 },
396 },
397 });
398 }
399 Diff::Tree {
400 diff: TreeDiff { diff },
401 }
402 }
403 loro::event::Diff::Counter(c) => Diff::Counter { diff: *c },
404 loro::event::Diff::Unknown => Diff::Unknown,
405 }
406 }
407}
408
409impl From<Diff> for loro::event::Diff<'static> {
410 fn from(value: Diff) -> Self {
411 match value {
412 Diff::List { diff } => {
413 loro::event::Diff::List(diff.into_iter().map(|i| i.into()).collect())
414 }
415 Diff::Text { diff } => {
416 loro::event::Diff::Text(diff.into_iter().map(|i| i.into()).collect())
417 }
418 Diff::Map { diff } => loro::event::Diff::Map(diff.into()),
419 Diff::Tree { diff } => loro::event::Diff::Tree(Cow::Owned(loro::TreeDiff {
420 diff: diff.diff.into_iter().map(|i| i.into()).collect(),
421 })),
422 Diff::Counter { diff } => loro::event::Diff::Counter(diff),
423 Diff::Unknown => loro::event::Diff::Unknown,
424 }
425 }
426}
427
428#[derive(Debug, Clone, Default)]
429pub struct DiffBatch(Arc<Mutex<loro::event::DiffBatch>>);
430
431impl DiffBatch {
432 pub fn new() -> Self {
433 Self(Default::default())
434 }
435
436 pub fn push(&self, cid: ContainerID, diff: Diff) -> Option<Diff> {
440 let mut batch = self.0.lock().unwrap();
441 if let Err(diff) = batch.push(cid.into(), diff.into()) {
442 Some((&diff).into())
443 } else {
444 None
445 }
446 }
447
448 pub fn get_diff(&self) -> Vec<ContainerIDAndDiff> {
456 let batch = self.0.lock().unwrap();
457 batch
458 .iter()
459 .map(|(id, diff)| ContainerIDAndDiff {
460 cid: id.into(),
461 diff: diff.into(),
462 })
463 .collect()
464 }
465}
466
467impl From<DiffBatch> for loro::event::DiffBatch {
468 fn from(value: DiffBatch) -> Self {
469 value.0.lock().unwrap().clone()
470 }
471}
472
473impl From<loro::event::DiffBatch> for DiffBatch {
474 fn from(value: loro::event::DiffBatch) -> Self {
475 Self(Arc::new(Mutex::new(value)))
476 }
477}
478
479pub struct ContainerIDAndDiff {
480 pub cid: ContainerID,
481 pub diff: Diff,
482}