use chrono::Utc;
use hlc_id::clock::HybridLogicalClock;
use hlc_id::id::HLCId;
use crate::Merge;
#[derive(Clone)]
pub struct LWWRegister<V: Clone> {
value: Option<V>,
timestamp: Option<HLCId>,
clock: HybridLogicalClock,
}
impl<V: Clone> LWWRegister<V> {
pub fn new(node_id: u16) -> Self {
Self {
value: None,
timestamp: None,
clock: HybridLogicalClock::new(node_id),
}
}
pub fn set(&mut self, value: V) {
let ts = Utc::now().timestamp_millis() as u64;
let id = HLCId::generate(&mut self.clock, ts);
self.value = Some(value);
self.timestamp = Some(id);
}
pub fn get(&self) -> Option<&V> {
self.value.as_ref()
}
}
impl<V: Clone> Merge for LWWRegister<V> {
fn merge(&mut self, other: &Self) {
match (&self.timestamp, &other.timestamp) {
(None, Some(_)) => {
self.value = other.value.clone();
self.timestamp = other.timestamp;
}
(Some(mine), Some(theirs)) => {
if mine.is_before(theirs) {
self.value = other.value.clone();
self.timestamp = other.timestamp;
}
}
_ => {}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_register_is_empty() {
let reg: LWWRegister<&str> = LWWRegister::new(1);
assert_eq!(reg.get(), None);
}
#[test]
fn test_set_and_get() {
let mut reg = LWWRegister::new(1);
reg.set("hello");
assert_eq!(reg.get(), Some(&"hello"));
}
#[test]
fn test_merge_empty_with_value() {
let mut a: LWWRegister<&str> = LWWRegister::new(1);
let mut b = LWWRegister::new(2);
b.set("hello");
a.merge(&b);
assert_eq!(a.get(), Some(&"hello"));
}
#[test]
fn test_last_writer_wins() {
let mut a = LWWRegister::new(1);
let mut b = LWWRegister::new(2);
a.set("first");
std::thread::sleep(std::time::Duration::from_millis(2));
b.set("second");
a.merge(&b);
assert_eq!(a.get(), Some(&"second"));
}
#[test]
fn test_merge_is_idempotent() {
let mut a = LWWRegister::new(1);
let mut b = LWWRegister::new(2);
a.set("hello");
b.set("world");
a.merge(&b);
let value_after_first = a.get().cloned();
a.merge(&b);
assert_eq!(a.get().cloned(), value_after_first);
}
}