Skip to main content

safemesh_crdt/crdt/
lww_register.rs

1// SafeMesh — delta-state CRDT convergence, built on crdt-lean.
2// Copyright (C) 2026 Ben Cassie
3// SPDX-License-Identifier: Apache-2.0
4
5use crate::{Crdt, MergeError, Mergeable, RecordId, WireError};
6
7/// Total-order dot for last-writer-wins registers.
8#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
9pub struct LwwDot {
10    pub timestamp: u64,
11    pub replica: u64,
12}
13
14/// One LWW register assignment.
15#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
16pub struct LwwEntry<V: Ord> {
17    pub dot: LwwDot,
18    pub value: V,
19}
20
21/// Delta for a last-writer-wins register.
22#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct LwwRegisterDelta<V: Ord> {
24    pub timestamp: u64,
25    pub replica: u64,
26    pub value: V,
27}
28
29/// Last-writer-wins register.
30///
31/// This is a flat tested-not-proven type. It is a max register over the total
32/// order `(timestamp, replica, value)`, which gives deterministic merge and
33/// tie-breaking. It is not currently part of the Lean-proven product surface.
34#[derive(Clone, Debug, PartialEq, Eq)]
35pub struct LwwRegister<V: Ord> {
36    entry: Option<LwwEntry<V>>,
37}
38
39impl<V: Ord> LwwRegister<V> {
40    pub fn new() -> Self {
41        LwwRegister { entry: None }
42    }
43
44    pub fn set(&mut self, timestamp: u64, replica: u64, value: V) {
45        self.apply_entry(LwwEntry {
46            dot: LwwDot { timestamp, replica },
47            value,
48        });
49    }
50
51    pub fn entry(&self) -> Option<&LwwEntry<V>> {
52        self.entry.as_ref()
53    }
54
55    pub fn value(&self) -> Option<&V> {
56        self.entry.as_ref().map(|entry| &entry.value)
57    }
58
59    fn apply_entry(&mut self, entry: LwwEntry<V>) {
60        match &self.entry {
61            Some(current) if current >= &entry => {}
62            _ => self.entry = Some(entry),
63        }
64    }
65}
66
67impl<V: Ord> Default for LwwRegister<V> {
68    fn default() -> Self {
69        Self::new()
70    }
71}
72
73impl<V: Ord + Clone> LwwRegister<V> {
74    pub fn merge(&mut self, other: &Self) {
75        if let Some(entry) = other.entry.clone() {
76            self.apply_entry(entry);
77        }
78    }
79}
80
81impl<V: Ord + Clone> Mergeable for LwwRegister<V> {
82    fn merge(&mut self, other: &Self) -> Result<(), MergeError> {
83        LwwRegister::merge(self, other);
84        Ok(())
85    }
86}
87
88impl<V: Ord + Clone> Crdt for LwwRegister<V> {
89    type Delta = LwwRegisterDelta<V>;
90
91    /// A record may only assign its author's dot. Losing assignments with a
92    /// matching author remain valid and are subsumed by the total-order rule.
93    fn validate_record(&self, id: RecordId, delta: &Self::Delta) -> Result<(), WireError> {
94        id.require_positive_sequence("LWW register")?;
95        if delta.replica != id.replica {
96            return Err(WireError::OwnershipViolation);
97        }
98        Ok(())
99    }
100
101    fn apply_delta(&mut self, delta: Self::Delta) {
102        self.set(delta.timestamp, delta.replica, delta.value);
103    }
104}