tirea_state/lattice/
g_counter.rs1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use super::Lattice;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(transparent)]
13pub struct GCounter(BTreeMap<String, u64>);
14
15impl GCounter {
16 pub fn new() -> Self {
18 Self(BTreeMap::new())
19 }
20
21 pub fn increment(&mut self, node: &str, delta: u64) {
23 let entry = self.0.entry(node.to_string()).or_insert(0);
24 *entry = entry.saturating_add(delta);
25 }
26
27 pub fn value(&self) -> u64 {
29 self.0.values().sum()
30 }
31
32 pub fn node_value(&self, node: &str) -> u64 {
34 self.0.get(node).copied().unwrap_or(0)
35 }
36}
37
38impl Default for GCounter {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44impl Lattice for GCounter {
45 fn merge(&self, other: &Self) -> Self {
46 let mut result = self.0.clone();
47 for (node, &count) in &other.0 {
48 let entry = result.entry(node.clone()).or_insert(0);
49 *entry = (*entry).max(count);
50 }
51 Self(result)
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use crate::lattice::assert_lattice_laws;
59
60 #[test]
61 fn new_is_zero() {
62 let c = GCounter::new();
63 assert_eq!(c.value(), 0);
64 }
65
66 #[test]
67 fn increment_and_value() {
68 let mut c = GCounter::new();
69 c.increment("a", 3);
70 c.increment("b", 5);
71 assert_eq!(c.value(), 8);
72 assert_eq!(c.node_value("a"), 3);
73 assert_eq!(c.node_value("b"), 5);
74 assert_eq!(c.node_value("c"), 0);
75 }
76
77 #[test]
78 fn increment_same_node() {
79 let mut c = GCounter::new();
80 c.increment("a", 3);
81 c.increment("a", 7);
82 assert_eq!(c.node_value("a"), 10);
83 assert_eq!(c.value(), 10);
84 }
85
86 #[test]
87 fn lattice_laws() {
88 let mut a = GCounter::new();
89 a.increment("x", 3);
90
91 let mut b = GCounter::new();
92 b.increment("y", 5);
93
94 let mut c = GCounter::new();
95 c.increment("x", 1);
96 c.increment("y", 2);
97
98 assert_lattice_laws(&a, &b, &c);
99 }
100
101 #[test]
102 fn lattice_laws_overlapping_nodes() {
103 let mut a = GCounter::new();
104 a.increment("n", 10);
105
106 let mut b = GCounter::new();
107 b.increment("n", 7);
108 b.increment("m", 3);
109
110 let mut c = GCounter::new();
111 c.increment("n", 5);
112 c.increment("m", 8);
113
114 assert_lattice_laws(&a, &b, &c);
115 }
116
117 #[test]
118 fn merge_per_node_max() {
119 let mut a = GCounter::new();
120 a.increment("x", 3);
121 a.increment("y", 1);
122
123 let mut b = GCounter::new();
124 b.increment("x", 1);
125 b.increment("y", 5);
126 b.increment("z", 2);
127
128 let merged = a.merge(&b);
129 assert_eq!(merged.node_value("x"), 3);
130 assert_eq!(merged.node_value("y"), 5);
131 assert_eq!(merged.node_value("z"), 2);
132 assert_eq!(merged.value(), 10);
133 }
134
135 #[test]
136 fn merge_empty() {
137 let a = GCounter::new();
138 let mut b = GCounter::new();
139 b.increment("x", 5);
140
141 assert_eq!(a.merge(&b).value(), 5);
142 assert_eq!(b.merge(&a).value(), 5);
143 assert_eq!(a.merge(&a).value(), 0);
144 }
145
146 #[test]
147 fn serde_roundtrip() {
148 let mut c = GCounter::new();
149 c.increment("a", 3);
150 c.increment("b", 5);
151
152 let json = serde_json::to_value(&c).unwrap();
153 assert_eq!(json, serde_json::json!({"a": 3, "b": 5}));
154
155 let back: GCounter = serde_json::from_value(json).unwrap();
156 assert_eq!(back, c);
157 }
158
159 #[test]
160 fn serde_empty() {
161 let c = GCounter::new();
162 let json = serde_json::to_value(&c).unwrap();
163 assert_eq!(json, serde_json::json!({}));
164
165 let back: GCounter = serde_json::from_value(json).unwrap();
166 assert_eq!(back, c);
167 }
168}