use crate::crdt::{Crdt, DeviceAware};
use crate::vector_clock::{ClockOrdering, VectorClock};
use crate::{DeviceId, SyncResult};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(bound(serialize = "T: Serialize"))]
#[serde(bound(deserialize = "T: serde::de::DeserializeOwned"))]
pub struct LwwRegister<T>
where
T: Clone,
{
value: T,
clock: VectorClock,
device_id: DeviceId,
}
impl<T> LwwRegister<T>
where
T: Clone,
{
pub fn new(device_id: DeviceId, initial_value: T) -> Self {
Self {
value: initial_value,
clock: VectorClock::new(device_id.clone()),
device_id,
}
}
pub fn get(&self) -> &T {
&self.value
}
pub fn set(&mut self, value: T) {
self.value = value;
self.clock.tick();
}
pub fn clock(&self) -> &VectorClock {
&self.clock
}
pub fn set_with_clock(&mut self, value: T, clock: VectorClock) {
match self.clock.compare(&clock) {
ClockOrdering::Before => {
self.value = value;
self.clock = clock;
}
ClockOrdering::Concurrent => {
if self.tie_break(&clock) {
self.value = value;
}
self.clock.merge(&clock);
}
_ => {
self.clock.merge(&clock);
}
}
}
fn tie_break(&self, other_clock: &VectorClock) -> bool {
other_clock.device_id() > &self.device_id
}
}
impl<T> Crdt for LwwRegister<T>
where
T: Clone + Serialize + for<'de> serde::Deserialize<'de>,
{
fn merge(&mut self, other: &Self) -> SyncResult<()> {
match self.clock.compare(&other.clock) {
ClockOrdering::Before => {
self.value = other.value.clone();
self.clock = other.clock.clone();
}
ClockOrdering::Concurrent => {
if self.tie_break(&other.clock) {
self.value = other.value.clone();
}
self.clock.merge(&other.clock);
}
_ => {
self.clock.merge(&other.clock);
}
}
Ok(())
}
fn dominated_by(&self, other: &Self) -> bool {
matches!(
self.clock.compare(&other.clock),
ClockOrdering::Before | ClockOrdering::Equal
)
}
}
impl<T> DeviceAware for LwwRegister<T>
where
T: Clone + Serialize + for<'de> serde::Deserialize<'de>,
{
fn device_id(&self) -> &DeviceId {
&self.device_id
}
fn set_device_id(&mut self, device_id: DeviceId) {
self.device_id = device_id.clone();
self.clock = self.clock.with_device_id(device_id);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lww_register_creation() {
let reg = LwwRegister::new("device-1".to_string(), 42);
assert_eq!(*reg.get(), 42);
assert_eq!(reg.device_id(), "device-1");
}
#[test]
fn test_lww_register_set() {
let mut reg = LwwRegister::new("device-1".to_string(), 42);
reg.set(100);
assert_eq!(*reg.get(), 100);
}
#[test]
fn test_lww_register_merge_before() {
let mut reg1 = LwwRegister::new("device-1".to_string(), 42);
let mut reg2 = LwwRegister::new("device-1".to_string(), 42);
reg2.set(100);
reg1.merge(®2).ok();
assert_eq!(*reg1.get(), 100);
}
#[test]
fn test_lww_register_merge_after() {
let mut reg1 = LwwRegister::new("device-1".to_string(), 42);
let reg2 = LwwRegister::new("device-1".to_string(), 42);
reg1.set(100);
reg1.merge(®2).ok();
assert_eq!(*reg1.get(), 100);
}
#[test]
fn test_lww_register_concurrent() {
let mut reg1 = LwwRegister::new("device-1".to_string(), 42);
let mut reg2 = LwwRegister::new("device-2".to_string(), 42);
reg1.set(100);
reg2.set(200);
let _initial = *reg1.get();
reg1.merge(®2).ok();
assert_eq!(*reg1.get(), 200);
}
#[test]
fn test_dominated_by() {
let reg1 = LwwRegister::new("device-1".to_string(), 42);
let mut reg2 = LwwRegister::new("device-1".to_string(), 42);
reg2.set(100);
assert!(reg1.dominated_by(®2));
assert!(!reg2.dominated_by(®1));
}
}