1use crate::CrdtMerge;
5use fxhash::{FxHashMap, FxHashSet};
6use serde::{Deserialize, Serialize};
7use std::hash::Hash;
8use uuid::Uuid;
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
15pub struct ORSet<T: Hash + Eq + Clone> {
16 elements: FxHashMap<T, FxHashSet<Uuid>>,
18 tombstones: FxHashSet<Uuid>,
20}
21
22impl<T: Hash + Eq + Clone> Default for ORSet<T> {
23 fn default() -> Self {
24 Self {
25 elements: FxHashMap::default(),
26 tombstones: FxHashSet::default(),
27 }
28 }
29}
30
31impl<T: Hash + Eq + Clone> ORSet<T> {
32 pub fn new() -> Self {
34 Self::default()
35 }
36
37 pub fn add(&mut self, element: T) -> Uuid {
39 let tag = Uuid::new_v4();
40 self.elements.entry(element).or_default().insert(tag);
41 tag
42 }
43
44 pub fn remove(&mut self, element: &T) {
47 if let Some(tags) = self.elements.get(element) {
48 self.tombstones.extend(tags.iter().copied());
49 }
50 }
51
52 fn is_visible(&self, tags: &FxHashSet<Uuid>) -> bool {
54 tags.iter().any(|tag| !self.tombstones.contains(tag))
55 }
56
57 pub fn contains(&self, element: &T) -> bool {
60 self.elements
61 .get(element)
62 .is_some_and(|tags| self.is_visible(tags))
63 }
64
65 pub fn elements(&self) -> Vec<T> {
67 self.elements
68 .iter()
69 .filter(|(_, tags)| self.is_visible(tags))
70 .map(|(elem, _)| elem.clone())
71 .collect()
72 }
73
74 pub fn len(&self) -> usize {
76 self.elements
77 .values()
78 .filter(|tags| self.is_visible(tags))
79 .count()
80 }
81
82 pub fn is_empty(&self) -> bool {
84 self.len() == 0
85 }
86}
87
88impl<T: Hash + Eq + Clone> CrdtMerge for ORSet<T> {
89 fn merge(&mut self, other: &Self) {
90 for (element, other_tags) in &other.elements {
92 self.elements
93 .entry(element.clone())
94 .or_default()
95 .extend(other_tags.iter().copied());
96 }
97 self.tombstones.extend(other.tombstones.iter().copied());
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn test_add_remove() {
108 let mut os = ORSet::new();
109 os.add("apple".to_string());
110 assert!(os.contains(&"apple".to_string()));
111
112 os.remove(&"apple".to_string());
113 assert!(!os.contains(&"apple".to_string()));
114 }
115
116 #[test]
117 fn test_add_wins() {
118 let mut a = ORSet::new();
119 a.add("apple".to_string());
120
121 let mut b = a.clone();
122 b.remove(&"apple".to_string());
123
124 a.add("apple".to_string());
126
127 a.merge(&b);
128
129 assert!(a.contains(&"apple".to_string()));
131 }
132
133 #[test]
134 fn test_merge() {
135 let mut a = ORSet::new();
136 a.add(1);
137 a.add(2);
138
139 let mut b = ORSet::new();
140 b.add(2);
141 b.add(3);
142
143 a.merge(&b);
144
145 let elements = a.elements();
146 assert!(elements.contains(&1));
147 assert!(elements.contains(&2));
148 assert!(elements.contains(&3));
149 assert_eq!(elements.len(), 3);
150 }
151}