radicle-heartwood-lfs/radicle-crdt/src/lwwreg.rs

135 lines
3.0 KiB
Rust

use num_traits::Bounded;
use crate::clock;
use crate::ord::Max;
use crate::Semilattice;
/// Last-Write-Wins Register.
///
/// In case of conflict, uses the [`Semilattice`] instance of `T` to merge.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LWWReg<T, C = clock::Lamport> {
clock: Max<C>,
value: T,
}
impl<T: Semilattice, C: PartialOrd> LWWReg<T, C> {
pub fn initial(value: T) -> Self
where
C: Default,
{
Self {
clock: Max::from(C::default()),
value,
}
}
pub fn new(value: T, clock: C) -> Self {
Self {
clock: Max::from(clock),
value,
}
}
pub fn set(&mut self, value: impl Into<T>, clock: C) {
let clock = Max::from(clock);
let value = value.into();
if clock == self.clock {
self.value.merge(value);
} else if clock > self.clock {
self.clock.merge(clock);
self.value = value;
}
}
pub fn get(&self) -> &T {
&self.value
}
pub fn clock(&self) -> &Max<C> {
&self.clock
}
pub fn into_inner(self) -> (T, C) {
(self.value, self.clock.into_inner())
}
}
impl<T: Default, C: Default + Bounded> Default for LWWReg<T, C> {
fn default() -> Self {
Self {
clock: Max::default(),
value: T::default(),
}
}
}
impl<T, C> Semilattice for LWWReg<T, C>
where
T: Semilattice,
C: PartialOrd,
{
fn merge(&mut self, other: Self) {
self.set(other.value, other.clock.into_inner());
}
}
#[cfg(test)]
mod tests {
use qcheck_macros::quickcheck;
use super::*;
use crate::Min;
#[quickcheck]
fn prop_semilattice(a: (Max<u8>, u16), b: (Max<u8>, u16), c: (Max<u8>, u16)) {
let a = LWWReg::new(a.0, a.1);
let b = LWWReg::new(b.0, b.1);
let c = LWWReg::new(c.0, c.1);
crate::test::assert_laws(&a, &b, &c);
}
#[test]
fn test_merge() {
let a = LWWReg::new(Max::from(0), 0);
let b = LWWReg::new(Max::from(1), 0);
assert_eq!(a.join(b).get(), &Max::from(1));
let a = LWWReg::new(Min::from(0), 0);
let b = LWWReg::new(Min::from(1), 0);
assert_eq!(a.join(b).get(), &Min::from(0));
}
#[test]
fn test_set_get() {
let mut reg = LWWReg::new(Max::from(42), 1);
assert_eq!(*reg.get(), Max::from(42));
reg.set(84, 0);
assert_eq!(*reg.get(), Max::from(42));
reg.set(84, 2);
assert_eq!(*reg.get(), Max::from(84));
// Smaller value, same clock: smaller value loses.
reg.set(42, 2);
assert_eq!(*reg.get(), Max::from(84));
// Bigger value, same clock: bigger value wins.
reg.set(168, 2);
assert_eq!(*reg.get(), Max::from(168));
// Smaller value, newer clock: smaller value wins.
reg.set(42, 3);
assert_eq!(*reg.get(), Max::from(42));
// Same value, newer clock: newer clock is set.
reg.set(42, 4);
assert_eq!(*reg.clock(), Max::from(4));
}
}