leptos_sync_core/crdt/basic/
lww_register.rs

1//! Last-Write-Wins Register implementation
2
3use super::{replica_id::ReplicaId, traits::{CRDT, Mergeable}};
4use serde::{Deserialize, Serialize};
5
6/// Last-Write-Wins Register
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
8pub struct LwwRegister<T> {
9    value: T,
10    timestamp: chrono::DateTime<chrono::Utc>,
11    replica_id: ReplicaId,
12}
13
14impl<T: Default> Default for LwwRegister<T> {
15    fn default() -> Self {
16        Self {
17            value: T::default(),
18            timestamp: chrono::Utc::now(),
19            replica_id: ReplicaId::default(),
20        }
21    }
22}
23
24impl<T> LwwRegister<T> {
25    pub fn new(value: T, replica_id: ReplicaId) -> Self {
26        Self {
27            value,
28            timestamp: chrono::Utc::now(),
29            replica_id,
30        }
31    }
32
33    pub fn value(&self) -> &T {
34        &self.value
35    }
36
37    pub fn timestamp(&self) -> chrono::DateTime<chrono::Utc> {
38        self.timestamp
39    }
40
41    pub fn replica_id(&self) -> ReplicaId {
42        self.replica_id
43    }
44
45    pub fn update(&mut self, value: T, replica_id: ReplicaId) {
46        self.value = value;
47        self.timestamp = chrono::Utc::now();
48        self.replica_id = replica_id;
49    }
50
51    pub fn with_timestamp(mut self, timestamp: chrono::DateTime<chrono::Utc>) -> Self {
52        self.timestamp = timestamp;
53        self
54    }
55}
56
57impl<T: Clone + PartialEq + Send + Sync> Mergeable for LwwRegister<T> {
58    type Error = std::io::Error;
59    
60    fn merge(&mut self, other: &Self) -> Result<(), Self::Error> {
61        if other.timestamp > self.timestamp || 
62           (other.timestamp == self.timestamp && other.replica_id.0 > self.replica_id.0) {
63            self.value = other.value.clone();
64            self.timestamp = other.timestamp;
65            self.replica_id = other.replica_id;
66        }
67        Ok(())
68    }
69    
70    fn has_conflict(&self, other: &Self) -> bool {
71        self.timestamp == other.timestamp && self.replica_id != other.replica_id
72    }
73}
74
75impl<T> CRDT for LwwRegister<T> {
76    fn replica_id(&self) -> &ReplicaId {
77        &self.replica_id
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn test_lww_register_creation() {
87        let replica_id = ReplicaId::default();
88        let register = LwwRegister::new("test_value", replica_id);
89        
90        assert_eq!(register.value(), &"test_value");
91        assert_eq!(register.replica_id(), replica_id);
92    }
93
94    #[test]
95    fn test_lww_register_update() {
96        let replica_id = ReplicaId::default();
97        let mut register = LwwRegister::new("old_value", replica_id);
98        
99        register.update("new_value", replica_id);
100        assert_eq!(register.value(), &"new_value");
101    }
102
103    #[test]
104    fn test_lww_register_merge() {
105        let mut reg1 = LwwRegister::new("value1", ReplicaId::default());
106        let reg2 = LwwRegister::new("value2", ReplicaId::default());
107        
108        // Wait a bit to ensure different timestamps
109        std::thread::sleep(std::time::Duration::from_millis(1));
110        
111        reg1.merge(&reg2).unwrap();
112        assert_eq!(reg1.value(), &"value2");
113    }
114
115    #[test]
116    fn test_lww_register_conflict_detection() {
117        let replica_id1 = ReplicaId::default();
118        let replica_id2 = ReplicaId::default();
119        
120        // Create registers with same timestamp but different replica IDs
121        let timestamp = chrono::Utc::now();
122        let reg1 = LwwRegister::new("value1", replica_id1).with_timestamp(timestamp);
123        let reg2 = LwwRegister::new("value2", replica_id2).with_timestamp(timestamp);
124        
125        assert!(reg1.has_conflict(&reg2));
126    }
127
128    #[test]
129    fn test_lww_register_serialization() {
130        let replica_id = ReplicaId::default();
131        let register = LwwRegister::new("test", replica_id);
132        
133        let serialized = serde_json::to_string(&register).unwrap();
134        let deserialized: LwwRegister<String> = serde_json::from_str(&serialized).unwrap();
135        
136        assert_eq!(register.value(), deserialized.value());
137        assert_eq!(register.replica_id(), deserialized.replica_id());
138    }
139}