Skip to main content

safemesh_crdt/crdt/
gset.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};
6use alloc::collections::BTreeSet;
7
8/// A grow-only set: merge = union.
9///
10/// This mirrors the upstream `crdt-lean` G-Set carrier (`Finset α`).
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct GSet<T: Ord> {
13    pub(super) elements: BTreeSet<T>,
14}
15
16impl<T: Ord> GSet<T> {
17    pub fn new() -> Self {
18        GSet {
19            elements: BTreeSet::new(),
20        }
21    }
22
23    pub fn insert(&mut self, element: T) {
24        self.elements.insert(element);
25    }
26
27    pub fn merge(&mut self, other: &Self)
28    where
29        T: Clone,
30    {
31        self.elements.extend(other.elements.iter().cloned());
32    }
33
34    pub fn contains(&self, element: &T) -> bool {
35        self.elements.contains(element)
36    }
37
38    pub fn elements(&self) -> &BTreeSet<T> {
39        &self.elements
40    }
41}
42
43impl<T: Ord> Default for GSet<T> {
44    fn default() -> Self {
45        Self::new()
46    }
47}
48
49impl<T: Ord + Clone> Mergeable for GSet<T> {
50    fn merge(&mut self, other: &Self) -> Result<(), MergeError> {
51        GSet::merge(self, other);
52        Ok(())
53    }
54}
55
56impl<T: Ord + Clone> Crdt for GSet<T> {
57    type Delta = T;
58
59    /// Accepts every positive-sequence record: every `T` is in a G-Set's domain, and a fresh set
60    /// inserts it. The only record replay ignores is an element already present,
61    /// and ignoring it is set idempotence (join with a member), not loss.
62    fn validate_record(&self, id: RecordId, _delta: &Self::Delta) -> Result<(), WireError> {
63        id.require_positive_sequence("G-Set")
64    }
65
66    fn apply_delta(&mut self, delta: Self::Delta) {
67        self.insert(delta);
68    }
69}