Skip to main content

crdt_kit/
gset.rs

1use alloc::collections::BTreeSet;
2
3use crate::{Crdt, DeltaCrdt};
4
5/// A grow-only set (G-Set).
6///
7/// Elements can be added but never removed. Merge is simply the union
8/// of both sets. This is the simplest set CRDT.
9///
10/// # Example
11///
12/// ```
13/// use crdt_kit::prelude::*;
14///
15/// let mut s1 = GSet::new();
16/// s1.insert("apple");
17/// s1.insert("banana");
18///
19/// let mut s2 = GSet::new();
20/// s2.insert("cherry");
21///
22/// s1.merge(&s2);
23/// assert_eq!(s1.len(), 3);
24/// assert!(s1.contains(&"cherry"));
25/// ```
26#[derive(Debug, Clone, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct GSet<T: Ord + Clone> {
29    elements: BTreeSet<T>,
30}
31
32impl<T: Ord + Clone> GSet<T> {
33    /// Create a new empty G-Set.
34    #[must_use]
35    pub fn new() -> Self {
36        Self {
37            elements: BTreeSet::new(),
38        }
39    }
40
41    /// Insert an element into the set.
42    ///
43    /// Returns `true` if the element was newly inserted.
44    pub fn insert(&mut self, value: T) -> bool {
45        self.elements.insert(value)
46    }
47
48    /// Check if the set contains an element.
49    #[must_use]
50    pub fn contains(&self, value: &T) -> bool {
51        self.elements.contains(value)
52    }
53
54    /// Get the number of elements in the set.
55    #[must_use]
56    pub fn len(&self) -> usize {
57        self.elements.len()
58    }
59
60    /// Check if the set is empty.
61    #[must_use]
62    pub fn is_empty(&self) -> bool {
63        self.elements.is_empty()
64    }
65
66    /// Iterate over the elements in the set.
67    pub fn iter(&self) -> impl Iterator<Item = &T> {
68        self.elements.iter()
69    }
70}
71
72impl<T: Ord + Clone> Default for GSet<T> {
73    fn default() -> Self {
74        Self::new()
75    }
76}
77
78impl<T: Ord + Clone> Crdt for GSet<T> {
79    fn merge(&mut self, other: &Self) {
80        for elem in &other.elements {
81            self.elements.insert(elem.clone());
82        }
83    }
84}
85
86/// Delta for [`GSet`]: elements present in self but not in other.
87#[derive(Debug, Clone, PartialEq, Eq)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
89pub struct GSetDelta<T: Ord + Clone> {
90    /// Elements to add.
91    pub elements: BTreeSet<T>,
92}
93
94impl<T: Ord + Clone> DeltaCrdt for GSet<T> {
95    type Delta = GSetDelta<T>;
96
97    fn delta(&self, other: &Self) -> GSetDelta<T> {
98        GSetDelta {
99            elements: self.elements.difference(&other.elements).cloned().collect(),
100        }
101    }
102
103    fn apply_delta(&mut self, delta: &GSetDelta<T>) {
104        for elem in &delta.elements {
105            self.elements.insert(elem.clone());
106        }
107    }
108}
109
110impl<T: Ord + Clone> IntoIterator for GSet<T> {
111    type Item = T;
112    type IntoIter = alloc::collections::btree_set::IntoIter<T>;
113
114    fn into_iter(self) -> Self::IntoIter {
115        self.elements.into_iter()
116    }
117}
118
119impl<T: Ord + Clone> FromIterator<T> for GSet<T> {
120    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
121        Self {
122            elements: BTreeSet::from_iter(iter),
123        }
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn new_set_is_empty() {
133        let s = GSet::<String>::new();
134        assert!(s.is_empty());
135        assert_eq!(s.len(), 0);
136    }
137
138    #[test]
139    fn insert_and_contains() {
140        let mut s = GSet::new();
141        assert!(s.insert("a"));
142        assert!(s.contains(&"a"));
143        assert!(!s.contains(&"b"));
144    }
145
146    #[test]
147    fn insert_duplicate_returns_false() {
148        let mut s = GSet::new();
149        assert!(s.insert("a"));
150        assert!(!s.insert("a"));
151        assert_eq!(s.len(), 1);
152    }
153
154    #[test]
155    fn merge_is_union() {
156        let mut s1 = GSet::new();
157        s1.insert(1);
158        s1.insert(2);
159
160        let mut s2 = GSet::new();
161        s2.insert(2);
162        s2.insert(3);
163
164        s1.merge(&s2);
165        assert_eq!(s1.len(), 3);
166        assert!(s1.contains(&1));
167        assert!(s1.contains(&2));
168        assert!(s1.contains(&3));
169    }
170
171    #[test]
172    fn merge_is_commutative() {
173        let mut s1 = GSet::new();
174        s1.insert("a");
175
176        let mut s2 = GSet::new();
177        s2.insert("b");
178
179        let mut left = s1.clone();
180        left.merge(&s2);
181
182        let mut right = s2.clone();
183        right.merge(&s1);
184
185        assert_eq!(left, right);
186    }
187
188    #[test]
189    fn merge_is_idempotent() {
190        let mut s1 = GSet::new();
191        s1.insert(1);
192
193        let mut s2 = GSet::new();
194        s2.insert(2);
195
196        s1.merge(&s2);
197        let after_first = s1.clone();
198        s1.merge(&s2);
199
200        assert_eq!(s1, after_first);
201    }
202
203    #[test]
204    fn delta_apply_equivalent_to_merge() {
205        let mut s1 = GSet::new();
206        s1.insert(1);
207        s1.insert(2);
208        s1.insert(3);
209
210        let mut s2 = GSet::new();
211        s2.insert(2);
212        s2.insert(4);
213
214        let mut full = s2.clone();
215        full.merge(&s1);
216
217        let mut via_delta = s2.clone();
218        let d = s1.delta(&s2);
219        via_delta.apply_delta(&d);
220
221        assert_eq!(full, via_delta);
222    }
223
224    #[test]
225    fn delta_is_empty_when_equal() {
226        let mut s1 = GSet::new();
227        s1.insert(1);
228        s1.insert(2);
229
230        let s2 = s1.clone();
231        let d = s1.delta(&s2);
232        assert!(d.elements.is_empty());
233    }
234
235    #[test]
236    fn from_iterator() {
237        let s: GSet<i32> = vec![1, 2, 3].into_iter().collect();
238        assert_eq!(s.len(), 3);
239    }
240}