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 *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 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}