use crate::{Crdt, DeltaCrdt, GCounter, GCounterDelta, NodeId};
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PNCounter {
increments: GCounter,
decrements: GCounter,
}
impl PNCounter {
pub fn new(actor: NodeId) -> Self {
Self {
increments: GCounter::new(actor),
decrements: GCounter::new(actor),
}
}
pub fn increment(&mut self) {
self.increments.increment();
}
pub fn decrement(&mut self) {
self.decrements.increment();
}
#[must_use]
pub fn value(&self) -> i64 {
self.increments.value() as i64 - self.decrements.value() as i64
}
}
impl Crdt for PNCounter {
fn merge(&mut self, other: &Self) {
self.increments.merge(&other.increments);
self.decrements.merge(&other.decrements);
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PNCounterDelta {
increments: GCounterDelta,
decrements: GCounterDelta,
}
impl DeltaCrdt for PNCounter {
type Delta = PNCounterDelta;
fn delta(&self, other: &Self) -> PNCounterDelta {
PNCounterDelta {
increments: self.increments.delta(&other.increments),
decrements: self.decrements.delta(&other.decrements),
}
}
fn apply_delta(&mut self, delta: &PNCounterDelta) {
self.increments.apply_delta(&delta.increments);
self.decrements.apply_delta(&delta.decrements);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_counter_is_zero() {
let c = PNCounter::new(1);
assert_eq!(c.value(), 0);
}
#[test]
fn increment_and_decrement() {
let mut c = PNCounter::new(1);
c.increment();
c.increment();
c.decrement();
assert_eq!(c.value(), 1);
}
#[test]
fn can_go_negative() {
let mut c = PNCounter::new(1);
c.decrement();
c.decrement();
assert_eq!(c.value(), -2);
}
#[test]
fn merge_different_actors() {
let mut c1 = PNCounter::new(1);
c1.increment();
c1.increment();
let mut c2 = PNCounter::new(2);
c2.decrement();
c1.merge(&c2);
assert_eq!(c1.value(), 1);
}
#[test]
fn merge_is_commutative() {
let mut c1 = PNCounter::new(1);
c1.increment();
let mut c2 = PNCounter::new(2);
c2.decrement();
c2.decrement();
let mut left = c1.clone();
left.merge(&c2);
let mut right = c2.clone();
right.merge(&c1);
assert_eq!(left.value(), right.value());
}
#[test]
fn merge_is_idempotent() {
let mut c1 = PNCounter::new(1);
c1.increment();
let mut c2 = PNCounter::new(2);
c2.decrement();
c1.merge(&c2);
let after_first = c1.clone();
c1.merge(&c2);
assert_eq!(c1, after_first);
}
#[test]
fn delta_apply_equivalent_to_merge() {
let mut c1 = PNCounter::new(1);
c1.increment();
c1.increment();
c1.decrement();
let mut c2 = PNCounter::new(2);
c2.decrement();
let mut full = c2.clone();
full.merge(&c1);
let mut via_delta = c2.clone();
let d = c1.delta(&c2);
via_delta.apply_delta(&d);
assert_eq!(full.value(), via_delta.value());
}
}