use super::{CrdtMerge, VectorClock};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MergeResult {
KeptSelf,
TookOther,
Concurrent, }
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct VCRegister<T: Clone> {
pub value: T,
pub clock: VectorClock,
}
impl<T: Clone> VCRegister<T> {
pub fn new(value: T, actor: &str) -> Self {
let mut clock = VectorClock::new();
clock.increment(actor);
Self { value, clock }
}
pub fn get(&self) -> &T {
&self.value
}
pub fn clock(&self) -> &VectorClock {
&self.clock
}
pub fn set(&mut self, value: T, actor: &str) {
self.clock.increment(actor);
self.value = value;
}
pub fn merge_register(&mut self, other: &VCRegister<T>) -> MergeResult {
match self.clock.partial_cmp(&other.clock) {
Some(Ordering::Less) => {
self.value = other.value.clone();
self.clock.merge(&other.clock);
MergeResult::TookOther
}
Some(Ordering::Greater) | Some(Ordering::Equal) => {
self.clock.merge(&other.clock);
MergeResult::KeptSelf
}
None => {
self.clock.merge(&other.clock);
MergeResult::Concurrent
}
}
}
}
impl<T: Clone> CrdtMerge for VCRegister<T> {
fn merge(&mut self, other: &Self) {
self.merge_register(other);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vc_register_causal() {
let mut r1 = VCRegister::new("A".to_string(), "node1"); let r2 = r1.clone();
r1.set("B".to_string(), "node1");
let mut r1_copy = r1.clone();
r1_copy.merge(&r2);
assert_eq!(r1_copy.get(), "B");
let mut r2_copy = r2.clone();
r2_copy.merge(&r1);
assert_eq!(r2_copy.get(), "B");
}
#[test]
fn test_vc_register_concurrent() {
let r_base = VCRegister::new("Base".to_string(), "node1");
let mut r1 = r_base.clone();
r1.set("A".to_string(), "node1");
let mut r2 = r_base.clone();
r2.set("B".to_string(), "node2");
let mut r1_copy = r1.clone();
let res = r1_copy.merge_register(&r2);
assert_eq!(res, MergeResult::Concurrent);
assert_eq!(r1_copy.get(), "A");
assert_eq!(r1_copy.clock.get("node1"), 2);
assert_eq!(r1_copy.clock.get("node2"), 1);
}
}