1use alloc::string::String;
2
3use crate::{Crdt, DeltaCrdt, GCounter, GCounterDelta};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub struct PNCounter {
31 increments: GCounter,
32 decrements: GCounter,
33}
34
35impl PNCounter {
36 pub fn new(actor: impl Into<String>) -> Self {
38 let actor = actor.into();
39 Self {
40 increments: GCounter::new(actor.clone()),
41 decrements: GCounter::new(actor),
42 }
43 }
44
45 pub fn increment(&mut self) {
47 self.increments.increment();
48 }
49
50 pub fn decrement(&mut self) {
52 self.decrements.increment();
53 }
54
55 #[must_use]
57 pub fn value(&self) -> i64 {
58 self.increments.value() as i64 - self.decrements.value() as i64
59 }
60}
61
62impl Crdt for PNCounter {
63 fn merge(&mut self, other: &Self) {
64 self.increments.merge(&other.increments);
65 self.decrements.merge(&other.decrements);
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72pub struct PNCounterDelta {
73 increments: GCounterDelta,
74 decrements: GCounterDelta,
75}
76
77impl DeltaCrdt for PNCounter {
78 type Delta = PNCounterDelta;
79
80 fn delta(&self, other: &Self) -> PNCounterDelta {
81 PNCounterDelta {
82 increments: self.increments.delta(&other.increments),
83 decrements: self.decrements.delta(&other.decrements),
84 }
85 }
86
87 fn apply_delta(&mut self, delta: &PNCounterDelta) {
88 self.increments.apply_delta(&delta.increments);
89 self.decrements.apply_delta(&delta.decrements);
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn new_counter_is_zero() {
99 let c = PNCounter::new("a");
100 assert_eq!(c.value(), 0);
101 }
102
103 #[test]
104 fn increment_and_decrement() {
105 let mut c = PNCounter::new("a");
106 c.increment();
107 c.increment();
108 c.decrement();
109 assert_eq!(c.value(), 1);
110 }
111
112 #[test]
113 fn can_go_negative() {
114 let mut c = PNCounter::new("a");
115 c.decrement();
116 c.decrement();
117 assert_eq!(c.value(), -2);
118 }
119
120 #[test]
121 fn merge_different_actors() {
122 let mut c1 = PNCounter::new("a");
123 c1.increment();
124 c1.increment();
125
126 let mut c2 = PNCounter::new("b");
127 c2.decrement();
128
129 c1.merge(&c2);
130 assert_eq!(c1.value(), 1); }
132
133 #[test]
134 fn merge_is_commutative() {
135 let mut c1 = PNCounter::new("a");
136 c1.increment();
137
138 let mut c2 = PNCounter::new("b");
139 c2.decrement();
140 c2.decrement();
141
142 let mut left = c1.clone();
143 left.merge(&c2);
144
145 let mut right = c2.clone();
146 right.merge(&c1);
147
148 assert_eq!(left.value(), right.value());
149 }
150
151 #[test]
152 fn merge_is_idempotent() {
153 let mut c1 = PNCounter::new("a");
154 c1.increment();
155
156 let mut c2 = PNCounter::new("b");
157 c2.decrement();
158
159 c1.merge(&c2);
160 let after_first = c1.clone();
161 c1.merge(&c2);
162
163 assert_eq!(c1, after_first);
164 }
165
166 #[test]
167 fn delta_apply_equivalent_to_merge() {
168 let mut c1 = PNCounter::new("a");
169 c1.increment();
170 c1.increment();
171 c1.decrement();
172
173 let mut c2 = PNCounter::new("b");
174 c2.decrement();
175
176 let mut full = c2.clone();
177 full.merge(&c1);
178
179 let mut via_delta = c2.clone();
180 let d = c1.delta(&c2);
181 via_delta.apply_delta(&d);
182
183 assert_eq!(full.value(), via_delta.value());
184 }
185}