loro_ffi/
event.rs

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