1use crate::{Crdt, GCounter};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct PNCounter {
28 increments: GCounter,
29 decrements: GCounter,
30}
31
32impl PNCounter {
33 pub fn new(actor: impl Into<String>) -> Self {
35 let actor = actor.into();
36 Self {
37 increments: GCounter::new(actor.clone()),
38 decrements: GCounter::new(actor),
39 }
40 }
41
42 pub fn increment(&mut self) {
44 self.increments.increment();
45 }
46
47 pub fn decrement(&mut self) {
49 self.decrements.increment();
50 }
51
52 #[must_use]
54 pub fn value(&self) -> i64 {
55 self.increments.value() as i64 - self.decrements.value() as i64
56 }
57}
58
59impl Crdt for PNCounter {
60 fn merge(&mut self, other: &Self) {
61 self.increments.merge(&other.increments);
62 self.decrements.merge(&other.decrements);
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn new_counter_is_zero() {
72 let c = PNCounter::new("a");
73 assert_eq!(c.value(), 0);
74 }
75
76 #[test]
77 fn increment_and_decrement() {
78 let mut c = PNCounter::new("a");
79 c.increment();
80 c.increment();
81 c.decrement();
82 assert_eq!(c.value(), 1);
83 }
84
85 #[test]
86 fn can_go_negative() {
87 let mut c = PNCounter::new("a");
88 c.decrement();
89 c.decrement();
90 assert_eq!(c.value(), -2);
91 }
92
93 #[test]
94 fn merge_different_actors() {
95 let mut c1 = PNCounter::new("a");
96 c1.increment();
97 c1.increment();
98
99 let mut c2 = PNCounter::new("b");
100 c2.decrement();
101
102 c1.merge(&c2);
103 assert_eq!(c1.value(), 1); }
105
106 #[test]
107 fn merge_is_commutative() {
108 let mut c1 = PNCounter::new("a");
109 c1.increment();
110
111 let mut c2 = PNCounter::new("b");
112 c2.decrement();
113 c2.decrement();
114
115 let mut left = c1.clone();
116 left.merge(&c2);
117
118 let mut right = c2.clone();
119 right.merge(&c1);
120
121 assert_eq!(left.value(), right.value());
122 }
123
124 #[test]
125 fn merge_is_idempotent() {
126 let mut c1 = PNCounter::new("a");
127 c1.increment();
128
129 let mut c2 = PNCounter::new("b");
130 c2.decrement();
131
132 c1.merge(&c2);
133 let after_first = c1.clone();
134 c1.merge(&c2);
135
136 assert_eq!(c1, after_first);
137 }
138}