1use std::{error, fmt};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{CmRDT, CvRDT};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
15pub struct LWWReg<V, M> {
16 pub val: V,
18 pub marker: M,
20}
21
22impl<V: Default, M: Default> Default for LWWReg<V, M> {
23 fn default() -> Self {
24 Self {
25 val: V::default(),
26 marker: M::default(),
27 }
28 }
29}
30
31#[derive(Debug, PartialEq)]
33pub enum Validation {
34 ConflictingMarker,
36}
37
38impl error::Error for Validation {
39 fn description(&self) -> &str {
40 match self {
41 Validation::ConflictingMarker => {
42 "A marker must be used exactly once, re-using the same marker breaks associativity"
43 }
44 }
45 }
46}
47
48impl fmt::Display for Validation {
49 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50 write!(f, "{:?}", self)
51 }
52}
53
54impl<V: PartialEq, M: Ord> CvRDT for LWWReg<V, M> {
55 type Validation = Validation;
56
57 fn validate_merge(&self, other: &Self) -> Result<(), Self::Validation> {
69 self.validate_update(&other.val, &other.marker)
70 }
71
72 fn merge(&mut self, LWWReg { val, marker }: Self) {
75 self.update(val, marker)
76 }
77}
78
79impl<V: PartialEq, M: Ord> CmRDT for LWWReg<V, M> {
80 type Op = Self;
83 type Validation = Validation;
84
85 fn validate_op(&self, op: &Self::Op) -> Result<(), Self::Validation> {
86 self.validate_update(&op.val, &op.marker)
87 }
88
89 fn apply(&mut self, op: Self::Op) {
90 self.merge(op)
91 }
92}
93
94impl<V: PartialEq, M: Ord> LWWReg<V, M> {
95 pub fn update(&mut self, val: V, marker: M) {
110 if self.marker < marker {
111 self.val = val;
112 self.marker = marker;
113 }
114 }
115
116 pub fn validate_update(&self, val: &V, marker: &M) -> Result<(), Validation> {
126 if &self.marker == marker && val != &self.val {
127 Err(Validation::ConflictingMarker)
128 } else {
129 Ok(())
130 }
131 }
132}
133
134#[cfg(test)]
135mod test {
136 use super::*;
137
138 #[test]
139 fn test_default() {
140 let reg = LWWReg::default();
141 assert_eq!(reg, LWWReg { val: "", marker: 0 });
142 }
143
144 #[test]
145 fn test_update() {
146 let mut reg = LWWReg {
147 val: 123,
148 marker: 0,
149 };
150
151 reg.update(32, 2);
154 assert_eq!(reg, LWWReg { val: 32, marker: 2 });
155
156 reg.update(57, 1);
159 assert_eq!(reg, LWWReg { val: 32, marker: 2 });
160
161 reg.update(32, 2);
164 assert_eq!(reg, LWWReg { val: 32, marker: 2 });
165
166 assert_eq!(
169 reg.validate_update(&4000, &2),
170 Err(Validation::ConflictingMarker)
171 );
172
173 reg.update(4000, 2);
175 assert_eq!(reg, LWWReg { val: 32, marker: 2 });
176 }
177
178 #[cfg(feature = "quickcheck")]
179 mod prop_tests {
180 use super::*;
181 use quickcheck::TestResult;
182 use quickcheck_macros::quickcheck;
183
184 #[quickcheck]
185 fn prop_associative(
186 r1_prim: (u8, u16),
187 r2_prim: (u8, u16),
188 r3_prim: (u8, u16),
189 ) -> TestResult {
190 let mut r1 = build_from_prim(r1_prim);
191 let mut r2 = build_from_prim(r2_prim);
192 let r3 = build_from_prim(r3_prim);
193
194 let has_conflicting_marker = (r1.marker == r2.marker && r1.val != r2.val)
195 || (r1.marker == r3.marker && r1.val != r3.val)
196 || (r2.marker == r3.marker && r2.val != r3.val);
197
198 if has_conflicting_marker {
199 return TestResult::discard();
200 }
201
202 let mut r1_snapshot = r1.clone();
203
204 r1.merge(r2.clone());
206 r1.merge(r3.clone());
207
208 r2.merge(r3);
210 r1_snapshot.merge(r2);
211
212 TestResult::from_bool(r1 == r1_snapshot)
214 }
215
216 #[quickcheck]
217 fn prop_commutative(r1_prim: (u8, u16), r2_prim: (u8, u16)) -> TestResult {
218 let mut r1 = build_from_prim(r1_prim);
219 let mut r2 = build_from_prim(r2_prim);
220
221 if r1.marker == r2.marker && r1.val != r2.val {
222 return TestResult::discard();
223 }
224 let r1_snapshot = r1.clone();
225
226 r1.merge(r2.clone());
228
229 r2.merge(r1_snapshot);
231
232 TestResult::from_bool(r1 == r2)
234 }
235
236 #[quickcheck]
237 fn prop_idempotent(r_prim: (u8, u16)) -> bool {
238 let mut r = build_from_prim(r_prim);
239 let r_snapshot = r.clone();
240
241 r.merge(r_snapshot.clone());
243 r == r_snapshot
245 }
246
247 fn build_from_prim(prim: (u8, u16)) -> LWWReg<u8, (u16, u8)> {
248 LWWReg {
250 val: prim.0,
251 marker: (prim.1, prim.0),
252 }
253 }
254 }
255}