use std::collections::BTreeSet;
use crate::datatypes::set::Tag;
use crate::datatypes::{ActorId, Crdt};
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct EwFlag {
adds: BTreeSet<Tag>,
removes: BTreeSet<Tag>,
actor_counters: std::collections::BTreeMap<ActorId, u64>,
}
impl EwFlag {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn enable(&mut self, actor: &ActorId) -> Tag {
let counter = self.actor_counters.entry(actor.clone()).or_insert(0);
*counter = counter.checked_add(1).expect("ew-flag counter overflow");
let tag = Tag {
actor: actor.clone(),
counter: *counter,
};
self.adds.insert(tag.clone());
tag
}
pub fn disable(&mut self) {
for tag in &self.adds {
self.removes.insert(tag.clone());
}
}
}
impl Crdt for EwFlag {
type Value = bool;
fn merge(&mut self, other: &Self) {
for (actor, &count) in &other.actor_counters {
let entry = self.actor_counters.entry(actor.clone()).or_insert(0);
if *entry < count {
*entry = count;
}
}
for tag in &other.adds {
self.adds.insert(tag.clone());
}
for tag in &other.removes {
self.removes.insert(tag.clone());
}
}
fn value(&self) -> bool {
self.adds.iter().any(|t| !self.removes.contains(t))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn aid(name: &str) -> ActorId {
ActorId::new("dc1", name)
}
#[test]
fn fresh_flag_is_disabled() {
let f = EwFlag::new();
assert!(!f.value());
}
#[test]
fn enable_makes_flag_true() {
let a = aid("a");
let mut f = EwFlag::new();
f.enable(&a);
assert!(f.value());
}
#[test]
fn disable_after_enable_clears_flag() {
let a = aid("a");
let mut f = EwFlag::new();
f.enable(&a);
f.disable();
assert!(!f.value());
}
#[test]
fn concurrent_enable_wins_over_disable() {
let a = aid("a");
let b = aid("b");
let mut shared = EwFlag::new();
shared.enable(&a);
let mut left = shared.clone();
left.disable();
assert!(!left.value());
let mut right = shared.clone();
right.enable(&b);
let mut merged = left.clone();
merged.merge(&right);
assert!(
merged.value(),
"enable-wins: concurrent enable must beat disable"
);
}
#[test]
fn merge_is_commutative() {
let a = aid("a");
let b = aid("b");
let mut x = EwFlag::new();
x.enable(&a);
x.disable();
let mut y = EwFlag::new();
y.enable(&b);
let mut left = x.clone();
left.merge(&y);
let mut right = y.clone();
right.merge(&x);
assert_eq!(left, right);
}
#[test]
fn merge_is_idempotent() {
let a = aid("a");
let mut x = EwFlag::new();
x.enable(&a);
let snap = x.clone();
x.merge(&snap);
assert_eq!(x, snap);
}
#[test]
fn enable_disable_enable_is_enabled() {
let a = aid("a");
let mut f = EwFlag::new();
f.enable(&a);
f.disable();
assert!(!f.value());
f.enable(&a);
assert!(f.value());
}
}