use chrono::Utc;
use hlc_id::{clock::HybridLogicalClock, id::HLCId};
use std::collections::{HashMap, HashSet};
use crate::Merge;
#[derive(Clone)]
pub struct ORSet<V: Eq + std::hash::Hash + Clone> {
elements: HashMap<V, HashSet<u128>>,
clock: HybridLogicalClock,
}
impl<V: Eq + std::hash::Hash + Clone> ORSet<V> {
pub fn new(node_id: u16) -> Self {
Self {
elements: HashMap::new(),
clock: HybridLogicalClock::new(node_id),
}
}
pub fn insert(&mut self, value: V) {
let ts = Utc::now().timestamp_millis() as u64;
let tag = HLCId::generate(&mut self.clock, ts);
self.elements
.entry(value)
.or_default()
.insert(tag.to_u128());
}
pub fn remove(&mut self, value: &V) {
self.elements.remove(value);
}
pub fn contains(&self, value: &V) -> bool {
self.elements
.get(value)
.map(|tags| !tags.is_empty())
.unwrap_or(false)
}
pub fn values(&self) -> HashSet<V> {
self.elements
.iter()
.filter(|(_, tags)| !tags.is_empty())
.map(|(value, _)| value.clone())
.collect()
}
}
impl<V: Eq + std::hash::Hash + Clone> Merge for ORSet<V> {
fn merge(&mut self, other: &Self) {
for (value, other_tags) in &other.elements {
let my_tags = self.elements.entry(value.clone()).or_default();
for tag in other_tags {
my_tags.insert(*tag);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_set_is_empty() {
let set: ORSet<&str> = ORSet::new(1);
assert!(!set.contains(&"hello"));
assert!(set.values().is_empty());
}
#[test]
fn test_insert_and_contains() {
let mut set = ORSet::new(1);
set.insert("hello");
assert!(set.contains(&"hello"));
assert!(!set.contains(&"world"));
}
#[test]
fn test_remove() {
let mut set = ORSet::new(1);
set.insert("hello");
set.remove(&"hello");
assert!(!set.contains(&"hello"));
}
#[test]
fn test_add_wins_over_remove() {
let mut a = ORSet::new(1);
let mut b = ORSet::new(2);
a.insert("hello");
b.merge(&a);
a.remove(&"hello");
std::thread::sleep(std::time::Duration::from_millis(2));
b.insert("hello");
a.merge(&b);
assert!(a.contains(&"hello"));
}
#[test]
fn test_merge_is_commutative() {
let mut a = ORSet::new(1);
let mut b = ORSet::new(2);
a.insert("x");
b.insert("y");
let mut a2 = a.clone();
let mut b2 = b.clone();
a2.merge(&b);
b2.merge(&a);
assert_eq!(a2.contains(&"x"), b2.contains(&"x"));
assert_eq!(a2.contains(&"y"), b2.contains(&"y"));
}
#[test]
fn test_merge_is_idempotent() {
let mut a = ORSet::new(1);
let mut b = ORSet::new(2);
a.insert("x");
b.insert("y");
a.merge(&b);
let count_after_first = a.values().len();
a.merge(&b);
assert_eq!(a.values().len(), count_after_first);
}
}