1use alloc::collections::BTreeMap;
2use alloc::string::String;
3
4use crate::{Crdt, DeltaCrdt};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct GCounter {
29 actor: String,
30 counts: BTreeMap<String, u64>,
31}
32
33impl GCounter {
34 pub fn new(actor: impl Into<String>) -> Self {
36 Self {
37 actor: actor.into(),
38 counts: BTreeMap::new(),
39 }
40 }
41
42 pub fn increment(&mut self) {
44 *self.counts.entry(self.actor.clone()).or_insert(0) += 1;
45 }
46
47 pub fn increment_by(&mut self, n: u64) {
49 *self.counts.entry(self.actor.clone()).or_insert(0) += n;
50 }
51
52 #[must_use]
54 pub fn value(&self) -> u64 {
55 self.counts.values().sum()
56 }
57
58 #[must_use]
60 pub fn actor(&self) -> &str {
61 &self.actor
62 }
63
64 #[must_use]
66 pub fn count_for(&self, actor: &str) -> u64 {
67 self.counts.get(actor).copied().unwrap_or(0)
68 }
69}
70
71impl Crdt for GCounter {
72 fn merge(&mut self, other: &Self) {
73 for (actor, &count) in &other.counts {
74 let entry = self.counts.entry(actor.clone()).or_insert(0);
75 *entry = (*entry).max(count);
76 }
77 }
78}
79
80#[derive(Debug, Clone, PartialEq, Eq)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
83pub struct GCounterDelta {
84 counts: BTreeMap<String, u64>,
85}
86
87impl DeltaCrdt for GCounter {
88 type Delta = GCounterDelta;
89
90 fn delta(&self, other: &Self) -> GCounterDelta {
91 let mut counts = BTreeMap::new();
92 for (actor, &self_count) in &self.counts {
93 let other_count = other.counts.get(actor).copied().unwrap_or(0);
94 if self_count > other_count {
95 counts.insert(actor.clone(), self_count);
96 }
97 }
98 GCounterDelta { counts }
99 }
100
101 fn apply_delta(&mut self, delta: &GCounterDelta) {
102 for (actor, &count) in &delta.counts {
103 let entry = self.counts.entry(actor.clone()).or_insert(0);
104 *entry = (*entry).max(count);
105 }
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn new_counter_is_zero() {
115 let c = GCounter::new("a");
116 assert_eq!(c.value(), 0);
117 }
118
119 #[test]
120 fn increment_increases_value() {
121 let mut c = GCounter::new("a");
122 c.increment();
123 assert_eq!(c.value(), 1);
124 c.increment();
125 assert_eq!(c.value(), 2);
126 }
127
128 #[test]
129 fn increment_by() {
130 let mut c = GCounter::new("a");
131 c.increment_by(5);
132 assert_eq!(c.value(), 5);
133 }
134
135 #[test]
136 fn merge_takes_max() {
137 let mut c1 = GCounter::new("a");
138 c1.increment();
139 c1.increment();
140
141 let mut c2 = GCounter::new("a");
142 c2.increment();
143
144 c1.merge(&c2);
146 assert_eq!(c1.value(), 2);
147 }
148
149 #[test]
150 fn merge_different_actors() {
151 let mut c1 = GCounter::new("a");
152 c1.increment();
153
154 let mut c2 = GCounter::new("b");
155 c2.increment();
156 c2.increment();
157
158 c1.merge(&c2);
159 assert_eq!(c1.value(), 3);
160 }
161
162 #[test]
163 fn merge_is_commutative() {
164 let mut c1 = GCounter::new("a");
165 c1.increment();
166
167 let mut c2 = GCounter::new("b");
168 c2.increment();
169 c2.increment();
170
171 let mut left = c1.clone();
172 left.merge(&c2);
173
174 let mut right = c2.clone();
175 right.merge(&c1);
176
177 assert_eq!(left.value(), right.value());
178 }
179
180 #[test]
181 fn merge_is_idempotent() {
182 let mut c1 = GCounter::new("a");
183 c1.increment();
184
185 let mut c2 = GCounter::new("b");
186 c2.increment();
187
188 c1.merge(&c2);
189 let after_first = c1.clone();
190 c1.merge(&c2);
191
192 assert_eq!(c1, after_first);
193 }
194
195 #[test]
196 fn count_for_actor() {
197 let mut c = GCounter::new("a");
198 c.increment();
199 c.increment();
200 assert_eq!(c.count_for("a"), 2);
201 assert_eq!(c.count_for("b"), 0);
202 }
203
204 #[test]
205 fn delta_contains_only_new_entries() {
206 let mut c1 = GCounter::new("a");
207 c1.increment();
208 c1.increment();
209
210 let mut c2 = GCounter::new("b");
211 c2.increment();
212
213 let d = c1.delta(&c2);
214 assert_eq!(d.counts.get("a"), Some(&2));
216 assert!(!d.counts.contains_key("b"));
217 }
218
219 #[test]
220 fn apply_delta_updates_state() {
221 let mut c1 = GCounter::new("a");
222 c1.increment();
223 c1.increment();
224
225 let mut c2 = GCounter::new("b");
226 c2.increment();
227
228 let d = c1.delta(&c2);
229 c2.apply_delta(&d);
230 assert_eq!(c2.value(), 3);
231 }
232
233 #[test]
234 fn delta_is_empty_when_equal() {
235 let mut c1 = GCounter::new("a");
236 c1.increment();
237
238 let c2 = c1.clone();
239 let d = c1.delta(&c2);
240 assert!(d.counts.is_empty());
241 }
242
243 #[test]
244 fn delta_equivalent_to_full_merge() {
245 let mut c1 = GCounter::new("a");
246 c1.increment();
247 c1.increment();
248
249 let mut c2 = GCounter::new("b");
250 c2.increment();
251
252 let mut full = c2.clone();
254 full.merge(&c1);
255
256 let mut via_delta = c2.clone();
258 let d = c1.delta(&c2);
259 via_delta.apply_delta(&d);
260
261 assert_eq!(full.value(), via_delta.value());
262 }
263}