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