oxigdal_sync/crdt/
pn_counter.rs1use crate::crdt::{Crdt, DeviceAware, GCounter};
6use crate::{DeviceId, SyncResult};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
31pub struct PnCounter {
32 positive: GCounter,
34 negative: GCounter,
36 device_id: DeviceId,
38}
39
40impl PnCounter {
41 pub fn new(device_id: DeviceId) -> Self {
47 Self {
48 positive: GCounter::new(device_id.clone()),
49 negative: GCounter::new(device_id.clone()),
50 device_id,
51 }
52 }
53
54 pub fn increment(&mut self, delta: u64) -> i64 {
64 self.positive.increment(delta);
65 self.value()
66 }
67
68 pub fn decrement(&mut self, delta: u64) -> i64 {
78 self.negative.increment(delta);
79 self.value()
80 }
81
82 pub fn inc(&mut self) -> i64 {
88 self.increment(1)
89 }
90
91 pub fn dec(&mut self) -> i64 {
97 self.decrement(1)
98 }
99
100 pub fn value(&self) -> i64 {
104 let pos = self.positive.value() as i64;
105 let neg = self.negative.value() as i64;
106 pos - neg
107 }
108
109 pub fn positive_counter(&self) -> &GCounter {
111 &self.positive
112 }
113
114 pub fn negative_counter(&self) -> &GCounter {
116 &self.negative
117 }
118
119 pub fn total_increments(&self) -> u64 {
121 self.positive.value()
122 }
123
124 pub fn total_decrements(&self) -> u64 {
126 self.negative.value()
127 }
128}
129
130impl Crdt for PnCounter {
131 fn merge(&mut self, other: &Self) -> SyncResult<()> {
132 self.positive.merge(&other.positive)?;
133 self.negative.merge(&other.negative)?;
134 Ok(())
135 }
136
137 fn dominated_by(&self, other: &Self) -> bool {
138 self.positive.dominated_by(&other.positive) && self.negative.dominated_by(&other.negative)
139 }
140}
141
142impl DeviceAware for PnCounter {
143 fn device_id(&self) -> &DeviceId {
144 &self.device_id
145 }
146
147 fn set_device_id(&mut self, device_id: DeviceId) {
148 self.device_id = device_id.clone();
149 self.positive.set_device_id(device_id.clone());
150 self.negative.set_device_id(device_id);
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_pn_counter_creation() {
160 let counter = PnCounter::new("device-1".to_string());
161 assert_eq!(counter.value(), 0);
162 assert_eq!(counter.device_id(), "device-1");
163 }
164
165 #[test]
166 fn test_pn_counter_increment() {
167 let mut counter = PnCounter::new("device-1".to_string());
168 counter.increment(5);
169 assert_eq!(counter.value(), 5);
170 counter.increment(3);
171 assert_eq!(counter.value(), 8);
172 }
173
174 #[test]
175 fn test_pn_counter_decrement() {
176 let mut counter = PnCounter::new("device-1".to_string());
177 counter.decrement(5);
178 assert_eq!(counter.value(), -5);
179 counter.decrement(3);
180 assert_eq!(counter.value(), -8);
181 }
182
183 #[test]
184 fn test_pn_counter_inc_dec() {
185 let mut counter = PnCounter::new("device-1".to_string());
186 counter.inc();
187 counter.inc();
188 counter.inc();
189 assert_eq!(counter.value(), 3);
190 counter.dec();
191 assert_eq!(counter.value(), 2);
192 }
193
194 #[test]
195 fn test_pn_counter_mixed_operations() {
196 let mut counter = PnCounter::new("device-1".to_string());
197 counter.increment(10);
198 counter.decrement(3);
199 counter.increment(5);
200 counter.decrement(2);
201 assert_eq!(counter.value(), 10); }
203
204 #[test]
205 fn test_pn_counter_merge() {
206 let mut counter1 = PnCounter::new("device-1".to_string());
207 let mut counter2 = PnCounter::new("device-2".to_string());
208
209 counter1.increment(10);
210 counter1.decrement(3);
211
212 counter2.increment(5);
213 counter2.decrement(2);
214
215 counter1.merge(&counter2).ok();
216
217 assert_eq!(counter1.value(), 10); }
219
220 #[test]
221 fn test_pn_counter_dominated_by() {
222 let mut counter1 = PnCounter::new("device-1".to_string());
223 let mut counter2 = PnCounter::new("device-1".to_string());
224
225 counter1.increment(3);
226 counter1.decrement(1);
227
228 counter2.increment(5);
229 counter2.decrement(2);
230
231 assert!(counter1.dominated_by(&counter2));
232 assert!(!counter2.dominated_by(&counter1));
233 }
234
235 #[test]
236 fn test_pn_counter_total_ops() {
237 let mut counter = PnCounter::new("device-1".to_string());
238 counter.increment(10);
239 counter.decrement(3);
240 counter.increment(5);
241
242 assert_eq!(counter.total_increments(), 15);
243 assert_eq!(counter.total_decrements(), 3);
244 assert_eq!(counter.value(), 12);
245 }
246}