leptos_sync_core/crdt/basic/
lww_map.rs1use super::{replica_id::ReplicaId, traits::{CRDT, Mergeable}, lww_register::LwwRegister};
4use std::collections::HashMap;
5use std::hash::Hash;
6
7#[derive(Debug, Clone)]
9pub struct LwwMap<K, V> {
10 data: HashMap<K, LwwRegister<V>>,
11}
12
13impl<K, V> LwwMap<K, V>
14where
15 K: Clone + Eq + Hash + Send + Sync,
16 V: Clone + PartialEq + Send + Sync,
17{
18 pub fn new() -> Self {
19 Self {
20 data: HashMap::new(),
21 }
22 }
23
24 pub fn insert(&mut self, key: K, value: V, replica_id: ReplicaId) {
25 let register = LwwRegister::new(value, replica_id);
26 self.data.insert(key, register);
27 }
28
29 pub fn get(&self, key: &K) -> Option<&V> {
30 self.data.get(key).map(|register| register.value())
31 }
32
33 pub fn get_register(&self, key: &K) -> Option<&LwwRegister<V>> {
34 self.data.get(key)
35 }
36
37 pub fn remove(&mut self, key: &K) -> Option<V> {
38 self.data.remove(key).map(|register| register.value().clone())
39 }
40
41 pub fn contains_key(&self, key: &K) -> bool {
42 self.data.contains_key(key)
43 }
44
45 pub fn len(&self) -> usize {
46 self.data.len()
47 }
48
49 pub fn is_empty(&self) -> bool {
50 self.data.is_empty()
51 }
52
53 pub fn keys(&self) -> impl Iterator<Item = &K> {
54 self.data.keys()
55 }
56
57 pub fn values(&self) -> impl Iterator<Item = &V> {
58 self.data.values().map(|register| register.value())
59 }
60
61 pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
62 self.data.iter().map(|(k, v)| (k, v.value()))
63 }
64}
65
66impl<K, V> Default for LwwMap<K, V>
67where
68 K: Clone + Eq + Hash + Send + Sync,
69 V: Clone + PartialEq + Send + Sync,
70{
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76impl<K, V> Mergeable for LwwMap<K, V>
77where
78 K: Clone + Eq + Hash + Send + Sync,
79 V: Clone + PartialEq + Send + Sync,
80{
81 type Error = std::io::Error;
82
83 fn merge(&mut self, other: &Self) -> Result<(), Self::Error> {
84 for (key, other_register) in &other.data {
85 match self.data.get_mut(key) {
86 Some(existing_register) => {
87 existing_register.merge(other_register)?;
88 }
89 None => {
90 self.data.insert(key.clone(), other_register.clone());
91 }
92 }
93 }
94 Ok(())
95 }
96
97 fn has_conflict(&self, other: &Self) -> bool {
98 for (key, other_register) in &other.data {
99 if let Some(existing_register) = self.data.get(key) {
100 if existing_register.has_conflict(other_register) {
101 return true;
102 }
103 }
104 }
105 false
106 }
107}
108
109impl<K, V> CRDT for LwwMap<K, V> {
110 fn replica_id(&self) -> &ReplicaId {
111 static DEFAULT_REPLICA: std::sync::LazyLock<ReplicaId> = std::sync::LazyLock::new(|| ReplicaId::from(uuid::Uuid::nil()));
114 &DEFAULT_REPLICA
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn test_lww_map_creation() {
124 let map: LwwMap<String, String> = LwwMap::new();
125 assert!(map.is_empty());
126 assert_eq!(map.len(), 0);
127 }
128
129 #[test]
130 fn test_lww_map_operations() {
131 let mut map = LwwMap::new();
132 let replica_id = ReplicaId::default();
133
134 map.insert("key1".to_string(), "value1".to_string(), replica_id);
135 assert_eq!(map.get(&"key1".to_string()), Some(&"value1".to_string()));
136 assert_eq!(map.len(), 1);
137
138 map.remove(&"key1".to_string());
139 assert_eq!(map.len(), 0);
140 }
141
142 #[test]
143 fn test_lww_map_merge() {
144 let mut map1 = LwwMap::new();
145 let mut map2 = LwwMap::new();
146 let replica_id1 = ReplicaId::default();
147 let replica_id2 = ReplicaId::default();
148
149 map1.insert("key1".to_string(), "value1".to_string(), replica_id1);
150 map2.insert("key2".to_string(), "value2".to_string(), replica_id2);
151
152 map1.merge(&map2).unwrap();
153
154 assert_eq!(map1.len(), 2);
155 assert_eq!(map1.get(&"key1".to_string()), Some(&"value1".to_string()));
156 assert_eq!(map1.get(&"key2".to_string()), Some(&"value2".to_string()));
157 }
158
159 #[test]
160 fn test_lww_map_iteration() {
161 let mut map = LwwMap::new();
162 let replica_id = ReplicaId::default();
163
164 map.insert("key1".to_string(), "value1".to_string(), replica_id);
165 map.insert("key2".to_string(), "value2".to_string(), replica_id);
166
167 let mut keys: Vec<_> = map.keys().collect();
168 keys.sort();
169 assert_eq!(keys, vec![&"key1".to_string(), &"key2".to_string()]);
170
171 let mut values: Vec<_> = map.values().collect();
172 values.sort();
173 assert_eq!(values, vec![&"value1".to_string(), &"value2".to_string()]);
174 }
175
176 #[test]
177 fn test_lww_map_conflict_detection() {
178 let mut map1 = LwwMap::new();
179 let mut map2 = LwwMap::new();
180 let replica_id1 = ReplicaId::default();
181 let replica_id2 = ReplicaId::default();
182
183 let timestamp = chrono::Utc::now();
185 let mut reg1 = LwwRegister::new("value1", replica_id1).with_timestamp(timestamp);
186 let reg2 = LwwRegister::new("value2", replica_id2).with_timestamp(timestamp);
187
188 map1.data.insert("key1".to_string(), reg1);
189 map2.data.insert("key1".to_string(), reg2);
190
191 assert!(map1.has_conflict(&map2));
192 }
193}