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 pub triggered_by: EventTriggerKind,
20 pub origin: String,
22 pub current_target: Option<ContainerID>,
24 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
44pub struct ContainerDiff {
46 pub target: ContainerID,
48 pub path: Vec<PathItem>,
50 pub is_unknown: bool,
52 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 List { diff: Vec<ListDiffItem> },
66 Text { diff: Vec<TextDelta> },
68 Map { diff: MapDelta },
70 Tree { diff: TreeDiff },
72 Counter { diff: f64 },
74 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 {
217 insert: Vec<Arc<dyn ValueOrContainer>>,
219 is_move: bool,
221 },
222 Delete {
224 delete: u32,
226 },
227 Retain {
231 retain: u32,
233 },
234}
235
236pub struct MapDelta {
237 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 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 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}