1use enum_as_inner::EnumAsInner;
2use fxhash::FxHashMap;
3use itertools::Itertools;
4use loro_delta::{array_vec::ArrayVec, delta_trait::DeltaAttr, DeltaItem, DeltaRope};
5use serde::{Deserialize, Serialize};
6use smallvec::SmallVec;
7
8use crate::{
9 container::richtext::richtext_state::RichtextStateChunk,
10 delta::{Delta, MapDelta, Meta, MovableListInnerDelta, ResolvedMapDelta, TreeDelta, TreeDiff},
11 diff_calc::DiffMode,
12 handler::ValueOrHandler,
13 op::SliceWithId,
14 utils::string_slice::StringSlice,
15 InternalString,
16};
17
18use std::{borrow::Cow, hash::Hash};
19
20use loro_common::{ContainerID, LoroValue, TreeID};
21
22use crate::{container::idx::ContainerIdx, version::Frontiers};
23
24#[derive(Debug, Clone)]
25pub struct ContainerDiff {
26 pub id: ContainerID,
27 pub path: Vec<(ContainerID, Index)>,
28 pub(crate) idx: ContainerIdx,
29 pub is_unknown: bool,
30 pub diff: Diff,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
35pub enum EventTriggerKind {
36 Local,
38 Import,
40 Checkout,
42}
43
44impl std::fmt::Display for EventTriggerKind {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 match self {
47 EventTriggerKind::Local => write!(f, "local"),
48 EventTriggerKind::Import => write!(f, "import"),
49 EventTriggerKind::Checkout => write!(f, "checkout"),
50 }
51 }
52}
53
54impl EventTriggerKind {
55 #[inline]
56 pub fn is_local(&self) -> bool {
57 matches!(self, EventTriggerKind::Local)
58 }
59
60 #[inline]
61 pub fn is_import(&self) -> bool {
62 matches!(self, EventTriggerKind::Import)
63 }
64
65 #[inline]
66 pub fn is_checkout(&self) -> bool {
67 matches!(self, EventTriggerKind::Checkout)
68 }
69}
70
71#[derive(Debug, Clone)]
72pub struct DiffEvent<'a> {
73 pub current_target: Option<ContainerID>,
75 pub events: &'a [&'a ContainerDiff],
77 pub event_meta: &'a DocDiff,
78}
79
80#[derive(Debug, Clone)]
86pub struct DocDiff {
87 pub from: Frontiers,
88 pub to: Frontiers,
89 pub origin: InternalString,
90 pub by: EventTriggerKind,
91 pub diff: Vec<ContainerDiff>,
92}
93
94#[derive(Debug, Clone)]
95pub(crate) struct InternalContainerDiff {
96 pub(crate) idx: ContainerIdx,
97 pub(crate) bring_back: bool,
99 pub(crate) is_container_deleted: bool,
100 pub(crate) diff: DiffVariant,
101 pub(crate) diff_mode: DiffMode,
103}
104
105#[derive(Default, Debug, Clone, EnumAsInner)]
106pub(crate) enum DiffVariant {
107 #[default]
108 None,
109 Internal(InternalDiff),
110 External(Diff),
111}
112
113#[derive(Debug, Clone)]
120pub(crate) struct InternalDocDiff<'a> {
121 pub(crate) origin: InternalString,
122 pub(crate) by: EventTriggerKind,
123 pub(crate) diff: Cow<'a, [InternalContainerDiff]>,
125 pub(crate) new_version: Cow<'a, Frontiers>,
126}
127
128impl InternalDocDiff<'_> {
129 pub fn into_owned(self) -> InternalDocDiff<'static> {
130 InternalDocDiff {
131 origin: self.origin,
132 by: self.by,
133 diff: Cow::Owned((*self.diff).to_owned()),
134 new_version: Cow::Owned((*self.new_version).to_owned()),
135 }
136 }
137
138 pub fn can_merge(&self, other: &Self) -> bool {
139 self.by == other.by
140 }
141}
142
143pub type Path = SmallVec<[Index; 4]>;
144
145#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, enum_as_inner::EnumAsInner)]
146pub enum Index {
147 Key(InternalString),
148 Seq(usize),
149 Node(TreeID),
150}
151
152impl std::fmt::Debug for Index {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 match self {
155 Self::Key(arg0) => write!(f, "Index::Key(\"{}\")", arg0),
156 Self::Seq(arg0) => write!(f, "Index::Seq({})", arg0),
157 Self::Node(arg0) => write!(f, "Index::Node({})", arg0),
158 }
159 }
160}
161
162impl std::fmt::Display for Index {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 match self {
165 Index::Key(key) => write!(f, "{}", key),
166 Index::Seq(s) => write!(f, "{}", s),
167 Index::Node(id) => write!(f, "{}@{}", id.peer, id.counter),
168 }
169 }
170}
171
172impl From<usize> for Index {
173 fn from(s: usize) -> Self {
174 Index::Seq(s)
175 }
176}
177
178impl TryFrom<&str> for Index {
179 type Error = &'static str;
180 fn try_from(s: &str) -> Result<Self, &'static str> {
181 if s.is_empty() {
182 return Ok(Index::Key(InternalString::default()));
183 }
184
185 let c = s.chars().next().unwrap();
186 if c.is_ascii_digit() {
187 if let Ok(seq) = s.parse::<usize>() {
188 Ok(Index::Seq(seq))
189 } else if let Ok(id) = s.try_into() {
190 Ok(Index::Node(id))
191 } else {
192 Ok(Index::Key(InternalString::from(s)))
193 }
194 } else {
195 Ok(Index::Key(InternalString::from(s)))
196 }
197 }
198}
199
200impl DiffVariant {
201 pub fn compose(self, other: Self) -> Result<Self, Self> {
202 match (self, other) {
203 (DiffVariant::Internal(a), DiffVariant::Internal(b)) => {
204 Ok(DiffVariant::Internal(a.compose(b)?))
205 }
206 (DiffVariant::External(a), DiffVariant::External(b)) => {
207 Ok(DiffVariant::External(a.compose(b)?))
208 }
209 (a, _) => Err(a),
210 }
211 }
212
213 pub fn is_empty(&self) -> bool {
214 match self {
215 DiffVariant::Internal(diff) => diff.is_empty(),
216 DiffVariant::External(diff) => diff.is_empty(),
217 DiffVariant::None => true,
218 }
219 }
220}
221
222#[non_exhaustive]
223#[derive(Clone, Debug, EnumAsInner)]
224pub(crate) enum InternalDiff {
225 ListRaw(Delta<SliceWithId>),
226 RichtextRaw(DeltaRope<RichtextStateChunk, ()>),
228 Map(MapDelta),
229 Tree(TreeDelta),
230 MovableList(MovableListInnerDelta),
231 #[cfg(feature = "counter")]
232 Counter(f64),
233 Unknown,
234}
235
236impl From<InternalDiff> for DiffVariant {
237 fn from(diff: InternalDiff) -> Self {
238 DiffVariant::Internal(diff)
239 }
240}
241
242#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
243pub struct ListDeltaMeta {
244 pub from_move: bool,
252}
253
254impl Meta for ListDeltaMeta {
255 fn is_empty(&self) -> bool {
256 !self.from_move
257 }
258
259 fn compose(
260 &mut self,
261 other: &Self,
262 type_pair: (crate::delta::DeltaType, crate::delta::DeltaType),
263 ) {
264 if let (crate::delta::DeltaType::Insert, crate::delta::DeltaType::Insert) = type_pair {
267 unreachable!()
268 }
269
270 self.from_move = self.from_move || other.from_move;
271 }
272
273 fn is_mergeable(&self, other: &Self) -> bool {
274 self.from_move == other.from_move
275 }
276
277 fn merge(&mut self, _other: &Self) {}
278}
279
280impl DeltaAttr for ListDeltaMeta {
281 fn compose(&mut self, other: &Self) {
282 self.from_move = self.from_move || other.from_move;
283 }
284
285 fn attr_is_empty(&self) -> bool {
286 !self.from_move
287 }
288}
289
290pub type ListDiffInsertItem = ArrayVec<ValueOrHandler, 8>;
291pub type ListDiffItem = DeltaItem<ListDiffInsertItem, ListDeltaMeta>;
292pub type ListDiff = DeltaRope<ListDiffInsertItem, ListDeltaMeta>;
293
294#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
295pub struct TextMeta(pub FxHashMap<String, LoroValue>);
296
297impl From<TextMeta> for FxHashMap<String, LoroValue> {
298 fn from(value: TextMeta) -> Self {
299 value.0
300 }
301}
302
303impl From<FxHashMap<String, LoroValue>> for TextMeta {
304 fn from(value: FxHashMap<String, LoroValue>) -> Self {
305 TextMeta(value)
306 }
307}
308
309pub type TextDiffItem = DeltaItem<StringSlice, TextMeta>;
310pub type TextDiff = DeltaRope<StringSlice, TextMeta>;
311
312impl DeltaAttr for TextMeta {
313 fn compose(&mut self, other: &Self) {
314 for (key, value) in other.0.iter() {
315 self.0.insert(key.clone(), value.clone());
316 }
317 }
318
319 fn attr_is_empty(&self) -> bool {
320 self.0.is_empty()
321 }
322}
323
324#[non_exhaustive]
334#[derive(Clone, Debug, EnumAsInner)]
335pub enum Diff {
336 List(ListDiff),
337 Text(TextDiff),
340 Map(ResolvedMapDelta),
341 Tree(TreeDiff),
342 #[cfg(feature = "counter")]
343 Counter(f64),
344 Unknown,
345}
346
347impl From<Diff> for DiffVariant {
348 fn from(diff: Diff) -> Self {
349 DiffVariant::External(diff)
350 }
351}
352
353impl InternalDiff {
354 pub(crate) fn is_empty(&self) -> bool {
355 match self {
356 InternalDiff::ListRaw(s) => s.is_empty(),
357 InternalDiff::RichtextRaw(t) => t.is_empty(),
358 InternalDiff::Map(m) => m.updated.is_empty(),
359 InternalDiff::Tree(t) => t.is_empty(),
360 InternalDiff::MovableList(t) => t.is_empty(),
361 #[cfg(feature = "counter")]
362 InternalDiff::Counter(c) => c.abs() < f64::EPSILON,
363 InternalDiff::Unknown => true,
364 }
365 }
366
367 pub(crate) fn compose(self, diff: InternalDiff) -> Result<Self, Self> {
368 match (self, diff) {
370 (InternalDiff::ListRaw(a), InternalDiff::ListRaw(b)) => {
371 Ok(InternalDiff::ListRaw(a.compose(b)))
372 }
373 (InternalDiff::RichtextRaw(a), InternalDiff::RichtextRaw(b)) => {
374 let mut ans = a.clone();
375 ans.compose(&b);
376 Ok(InternalDiff::RichtextRaw(ans))
377 }
378 (InternalDiff::Map(a), InternalDiff::Map(b)) => Ok(InternalDiff::Map(a.compose(b))),
379 (InternalDiff::Tree(a), InternalDiff::Tree(b)) => Ok(InternalDiff::Tree(a.compose(b))),
380 (a, _) => Err(a),
381 }
382 }
383}
384
385impl Diff {
386 pub fn compose_ref(&mut self, diff: &Diff) {
387 match (self, diff) {
389 (Diff::List(a), Diff::List(b)) => {
390 a.compose(b);
391 }
392 (Diff::Text(a), Diff::Text(b)) => {
393 a.compose(b);
394 }
395 (Diff::Map(a), Diff::Map(b)) => {
396 *a = a.clone().compose(b.clone());
397 }
398 (Diff::Tree(a), Diff::Tree(b)) => {
399 *a = a.clone().compose(b.clone());
400 }
401 #[cfg(feature = "counter")]
402 (Diff::Counter(a), Diff::Counter(b)) => *a += b,
403 (_, _) => unreachable!(),
404 }
405 }
406
407 pub fn compose(self, diff: Diff) -> Result<Self, Self> {
408 match (self, diff) {
410 (Diff::List(mut a), Diff::List(b)) => {
411 a.compose(&b);
412 Ok(Diff::List(a))
413 }
414 (Diff::Text(mut a), Diff::Text(b)) => {
415 a.compose(&b);
416 Ok(Diff::Text(a))
417 }
418 (Diff::Map(a), Diff::Map(b)) => Ok(Diff::Map(a.compose(b))),
419
420 (Diff::Tree(a), Diff::Tree(b)) => Ok(Diff::Tree(a.compose(b))),
421 #[cfg(feature = "counter")]
422 (Diff::Counter(a), Diff::Counter(b)) => Ok(Diff::Counter(a + b)),
423 (a, _) => Err(a),
424 }
425 }
426
427 pub fn transform(&mut self, other: &Self, left_prior: bool) {
429 match (self, other) {
430 (Diff::List(a), Diff::List(b)) => a.transform_(b, left_prior),
431 (Diff::Text(a), Diff::Text(b)) => a.transform_(b, left_prior),
432 (Diff::Map(a), Diff::Map(b)) => a.transform(b, left_prior),
433 (Diff::Tree(a), Diff::Tree(b)) => a.transform(b, left_prior),
434 #[cfg(feature = "counter")]
435 (Diff::Counter(a), Diff::Counter(b)) => {
436 if left_prior {
437 *a += b;
438 } else {
439 *a -= b;
440 }
441 }
442 _ => {}
443 }
444 }
445
446 pub fn is_empty(&self) -> bool {
447 match self {
448 Diff::List(s) => s.is_empty(),
449 Diff::Text(t) => t.is_empty(),
450 Diff::Map(m) => m.updated.is_empty(),
451 Diff::Tree(t) => t.diff.is_empty(),
452 #[cfg(feature = "counter")]
453 Diff::Counter(c) => c.abs() < f64::EPSILON,
454 Diff::Unknown => true,
455 }
456 }
457
458 #[allow(unused)]
459 pub(crate) fn concat(self, diff: Diff) -> Diff {
460 match (self, diff) {
461 (Diff::List(mut a), Diff::List(b)) => {
462 a.compose(&b);
463 Diff::List(a)
464 }
465 (Diff::Text(mut a), Diff::Text(b)) => {
466 a.compose(&b);
467 Diff::Text(a)
468 }
469 (Diff::Map(a), Diff::Map(b)) => {
470 let mut a = a;
471 for (k, v) in b.updated {
472 a = a.with_entry(k, v);
473 }
474 Diff::Map(a)
475 }
476
477 (Diff::Tree(a), Diff::Tree(b)) => Diff::Tree(a.extend(b.diff)),
478 #[cfg(feature = "counter")]
479 (Diff::Counter(a), Diff::Counter(b)) => Diff::Counter(a + b),
480 _ => unreachable!(),
481 }
482 }
483
484 pub(crate) fn transform_cursor(&self, pos: usize, left_prior: bool) -> usize {
486 match self {
487 Diff::List(list) => list.transform_pos(pos, left_prior),
488 Diff::Text(text) => text.transform_pos(pos, left_prior),
489 _ => pos,
490 }
491 }
492}
493
494pub fn str_to_path(s: &str) -> Option<Vec<Index>> {
495 s.split('/').map(|x| x.try_into()).try_collect().ok()
496}
497
498pub fn path_to_str(path: &[Index]) -> String {
499 path.iter().map(|x| x.to_string()).join("/")
500}
501
502#[cfg(test)]
503mod test {
504 use std::sync::Arc;
505
506 use itertools::Itertools;
507 use loro_common::LoroValue;
508
509 use crate::{ApplyDiff, LoroDoc};
510
511 #[test]
512 fn test_text_event() {
513 let loro = LoroDoc::new();
514 let _g = loro.subscribe_root(Arc::new(|event| {
515 let mut value = LoroValue::String(Default::default());
516 value.apply_diff(&event.events.iter().map(|x| x.diff.clone()).collect_vec());
517 assert_eq!(value, "h223ello".into());
518 }));
519 let mut txn = loro.txn().unwrap();
520 let text = loro.get_text("id");
521 text.insert_with_txn(&mut txn, 0, "hello").unwrap();
522 text.insert_with_txn(&mut txn, 1, "223").unwrap();
523 txn.commit().unwrap();
524 }
525}