Skip to main content

loro_internal/delta/
map.rs

1use rustc_hash::{FxHashMap, FxHashSet};
2use serde::Serialize;
3use std::fmt::Debug;
4
5use crate::{InternalString, LoroValue};
6
7#[derive(Clone, Debug, Serialize)]
8pub struct ValuePair<T> {
9    pub old: T,
10    pub new: T,
11}
12
13impl From<(LoroValue, LoroValue)> for ValuePair<LoroValue> {
14    fn from((old, new): (LoroValue, LoroValue)) -> Self {
15        ValuePair { old, new }
16    }
17}
18
19#[derive(Clone, Debug, Serialize)]
20pub struct MapDiff<T> {
21    pub added: FxHashMap<InternalString, T>,
22    pub updated: FxHashMap<InternalString, ValuePair<T>>,
23    pub deleted: FxHashMap<InternalString, T>,
24}
25
26impl<T> MapDiff<T> {
27    pub fn new() -> Self {
28        Self::default()
29    }
30
31    pub fn insert(mut self, key: InternalString, value: T) -> Self {
32        self.deleted.remove(&key);
33        self.added.insert(key, value);
34        self
35    }
36
37    pub fn delete(mut self, key: &InternalString, value: T) -> Self {
38        self.added.remove(key);
39        self.deleted.insert(key.clone(), value);
40        self
41    }
42
43    pub fn compose(mut self, other: Self) -> Self {
44        for (k, v) in other.added.into_iter() {
45            if let Some(dv) = self.deleted.remove(&k) {
46                self.updated.insert(k, ValuePair { old: dv, new: v });
47            } else if let Some(ValuePair { old: _, new }) = self.updated.get_mut(&k) {
48                // should not happen in transaction
49                *new = v;
50            } else {
51                self.added.insert(k, v);
52            }
53        }
54
55        for (k, v) in other.deleted.into_iter() {
56            if let Some(av) = self.added.remove(&k) {
57                self.deleted.insert(k, av);
58            } else if let Some(ValuePair { old, .. }) = self.updated.remove(&k) {
59                self.deleted.insert(k, old);
60            } else {
61                // delete old value
62                self.deleted.insert(k, v);
63            }
64        }
65
66        for (k, ValuePair { old, new }) in other.updated.into_iter() {
67            if let Some(av) = self.added.get_mut(&k) {
68                *av = new;
69            } else if let Some(dv) = self.deleted.remove(&k) {
70                self.updated.insert(k, ValuePair { old: dv, new });
71            } else if let Some(ValuePair { old: _, new: n }) = self.updated.get_mut(&k) {
72                *n = new
73            } else {
74                self.updated.insert(k, ValuePair { old, new });
75            }
76        }
77        self
78    }
79}
80
81#[derive(Clone, Debug, Serialize)]
82#[allow(dead_code)]
83pub struct MapDiffRaw<T> {
84    pub(crate) added: FxHashMap<InternalString, T>,
85    pub(crate) deleted: FxHashSet<InternalString>,
86}
87
88impl<T> Default for MapDiff<T> {
89    fn default() -> Self {
90        Self {
91            added: Default::default(),
92            updated: Default::default(),
93            deleted: Default::default(),
94        }
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::{MapDiff, ValuePair};
101    use crate::InternalString;
102
103    fn key(value: &str) -> InternalString {
104        value.into()
105    }
106
107    fn map_diff(
108        added: &[(&str, i32)],
109        updated: &[(&str, (i32, i32))],
110        deleted: &[(&str, i32)],
111    ) -> MapDiff<i32> {
112        let mut ans = MapDiff::new();
113        for (k, v) in added {
114            ans.added.insert(key(k), *v);
115        }
116        for (k, (old, new)) in updated {
117            ans.updated.insert(
118                key(k),
119                ValuePair {
120                    old: *old,
121                    new: *new,
122                },
123            );
124        }
125        for (k, v) in deleted {
126            ans.deleted.insert(key(k), *v);
127        }
128        ans
129    }
130
131    fn assert_map_diff_eq(actual: MapDiff<i32>, expected: MapDiff<i32>) {
132        assert_eq!(actual.added, expected.added);
133        assert_eq!(actual.deleted, expected.deleted);
134
135        assert_eq!(actual.updated.len(), expected.updated.len());
136        for (key, expected_pair) in expected.updated {
137            let actual_pair = actual.updated.get(&key).unwrap_or_else(|| {
138                panic!("missing updated entry for key {:?}", key);
139            });
140            assert_eq!(actual_pair.old, expected_pair.old);
141            assert_eq!(actual_pair.new, expected_pair.new);
142        }
143    }
144
145    #[test]
146    fn insert_replaces_previous_delete_marker() {
147        let diff = map_diff(&[], &[], &[("key", 1)]).insert(key("key"), 2);
148        assert_map_diff_eq(diff, map_diff(&[("key", 2)], &[], &[]));
149    }
150
151    #[test]
152    fn delete_removes_pending_add_and_records_deleted_value() {
153        let diff = map_diff(&[("key", 1)], &[], &[]).delete(&key("key"), 1);
154        assert_map_diff_eq(diff, map_diff(&[], &[], &[("key", 1)]));
155    }
156
157    #[test]
158    fn compose_add_then_update_keeps_latest_value_in_added() {
159        let lhs = map_diff(&[("key", 1)], &[], &[]);
160        let rhs = map_diff(&[], &[("key", (1, 2))], &[]);
161
162        assert_map_diff_eq(lhs.compose(rhs), map_diff(&[("key", 2)], &[], &[]));
163    }
164
165    #[test]
166    fn compose_delete_then_add_becomes_update_with_original_old_value() {
167        let lhs = map_diff(&[], &[], &[("key", 1)]);
168        let rhs = map_diff(&[("key", 2)], &[], &[]);
169
170        assert_map_diff_eq(lhs.compose(rhs), map_diff(&[], &[("key", (1, 2))], &[]));
171    }
172
173    #[test]
174    fn compose_update_then_delete_keeps_initial_old_value() {
175        let lhs = map_diff(&[], &[("key", (1, 2))], &[]);
176        let rhs = map_diff(&[], &[], &[("key", 2)]);
177
178        assert_map_diff_eq(lhs.compose(rhs), map_diff(&[], &[], &[("key", 1)]));
179    }
180
181    #[test]
182    fn compose_multiple_updates_preserves_first_old_and_latest_new() {
183        let lhs = map_diff(&[], &[("key", (1, 2))], &[]);
184        let rhs = map_diff(&[], &[("key", (2, 3))], &[]);
185        let third = map_diff(&[], &[("key", (3, 4))], &[]);
186
187        assert_map_diff_eq(
188            lhs.compose(rhs).compose(third),
189            map_diff(&[], &[("key", (1, 4))], &[]),
190        );
191    }
192
193    #[test]
194    fn compose_is_key_local_and_preserves_other_entries() {
195        let lhs = map_diff(&[("added", 10)], &[("updated", (1, 2))], &[("deleted", 3)]);
196        let rhs = map_diff(&[("added", 11)], &[("updated", (2, 3))], &[("deleted", 4)]);
197
198        assert_map_diff_eq(
199            lhs.compose(rhs),
200            map_diff(&[("added", 11)], &[("updated", (1, 3))], &[("deleted", 4)]),
201        );
202    }
203}