leptos_sync_core/crdt/advanced/
lseq.rs

1//! LSEQ (Logoot Sequence) for ordered sequences
2
3use super::common::{PositionId, AdvancedCrdtError};
4use super::super::{CRDT, Mergeable, ReplicaId};
5use serde::{Deserialize, Serialize};
6use std::collections::BTreeMap;
7
8/// LSEQ (Logoot Sequence) element
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub struct LseqElement<T> {
11    /// Unique position identifier
12    pub position: PositionId,
13    /// Element value
14    pub value: T,
15    /// Whether the element is visible (not deleted)
16    pub visible: bool,
17}
18
19impl<T> LseqElement<T> {
20    /// Create a new LSEQ element
21    pub fn new(position: PositionId, value: T) -> Self {
22        Self {
23            position,
24            value,
25            visible: true,
26        }
27    }
28}
29
30/// LSEQ (Logoot Sequence) for ordered sequences
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32pub struct Lseq<T> {
33    /// Replica ID
34    replica_id: ReplicaId,
35    /// Elements indexed by position
36    elements: BTreeMap<PositionId, LseqElement<T>>,
37    /// Logical timestamp counter
38    timestamp_counter: u64,
39    /// Disambiguation counter
40    disambiguation_counter: u64,
41}
42
43impl<T: Clone + PartialEq> Lseq<T> {
44    /// Create a new LSEQ
45    pub fn new(replica_id: ReplicaId) -> Self {
46        Self {
47            replica_id,
48            elements: BTreeMap::new(),
49            timestamp_counter: 0,
50            disambiguation_counter: 0,
51        }
52    }
53    
54    /// Insert an element at the given position
55    pub fn insert(&mut self, value: T, _position: Option<PositionId>) -> Result<PositionId, AdvancedCrdtError> {
56        self.timestamp_counter += 1;
57        self.disambiguation_counter += 1;
58        
59        let new_position = PositionId::new(
60            self.replica_id.clone(),
61            self.timestamp_counter,
62            self.disambiguation_counter,
63        );
64        
65        let element = LseqElement::new(new_position.clone(), value);
66        self.elements.insert(new_position.clone(), element);
67        
68        Ok(new_position)
69    }
70    
71    /// Delete an element at the given position
72    pub fn delete(&mut self, position: &PositionId) -> Result<(), AdvancedCrdtError> {
73        if let Some(element) = self.elements.get_mut(position) {
74            element.visible = false;
75            Ok(())
76        } else {
77            Err(AdvancedCrdtError::ElementNotFound(format!("Position {:?}", position)))
78        }
79    }
80    
81    /// Get the visible elements in order
82    pub fn to_vec(&self) -> Vec<T> {
83        self.elements.values()
84            .filter(|e| e.visible)
85            .map(|e| e.value.clone())
86            .collect()
87    }
88    
89    /// Get element count
90    pub fn len(&self) -> usize {
91        self.elements.len()
92    }
93    
94    /// Check if LSEQ is empty
95    pub fn is_empty(&self) -> bool {
96        self.elements.is_empty()
97    }
98    
99    /// Get all elements (for debugging/inspection)
100    pub fn get_elements(&self) -> &BTreeMap<PositionId, LseqElement<T>> {
101        &self.elements
102    }
103}
104
105impl<T: Clone + PartialEq> CRDT for Lseq<T> {
106    fn replica_id(&self) -> &ReplicaId {
107        &self.replica_id
108    }
109}
110
111impl<T: Clone + PartialEq + Send + Sync> Mergeable for Lseq<T> {
112    type Error = AdvancedCrdtError;
113    
114    fn merge(&mut self, other: &Self) -> Result<(), Self::Error> {
115        // Merge all elements from other LSEQ
116        for (position, other_element) in &other.elements {
117            if let Some(self_element) = self.elements.get_mut(position) {
118                // Element exists in both, keep the one with higher timestamp
119                if other_element.position.timestamp > self_element.position.timestamp {
120                    *self_element = other_element.clone();
121                }
122            } else {
123                // Element only exists in other, add it
124                self.elements.insert(position.clone(), other_element.clone());
125            }
126        }
127        
128        Ok(())
129    }
130    
131    fn has_conflict(&self, other: &Self) -> bool {
132        // Check for conflicting elements (same position, different values)
133        for (position, self_element) in &self.elements {
134            if let Some(other_element) = other.elements.get(position) {
135                if self_element.value != other_element.value {
136                    return true;
137                }
138            }
139        }
140        false
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147    use super::super::super::ReplicaId;
148    use uuid::Uuid;
149    
150    fn create_replica(id: u64) -> ReplicaId {
151        ReplicaId::from(Uuid::from_u64_pair(0, id))
152    }
153    
154    #[test]
155    fn test_lseq_creation() {
156        let replica_id = create_replica(1);
157        let lseq = Lseq::<String>::new(replica_id.clone());
158        
159        assert_eq!(lseq.replica_id(), &replica_id);
160        assert!(lseq.is_empty());
161        assert_eq!(lseq.len(), 0);
162    }
163    
164    #[test]
165    fn test_lseq_insert_and_delete() {
166        let replica_id = create_replica(1);
167        let mut lseq = Lseq::<String>::new(replica_id);
168        
169        // Insert elements
170        let pos1 = lseq.insert("hello".to_string(), None).unwrap();
171        let pos2 = lseq.insert("world".to_string(), None).unwrap();
172        let pos3 = lseq.insert("!".to_string(), None).unwrap();
173        
174        assert_eq!(lseq.len(), 3);
175        let elements = lseq.to_vec();
176        assert!(elements.contains(&"hello".to_string()));
177        assert!(elements.contains(&"world".to_string()));
178        assert!(elements.contains(&"!".to_string()));
179        
180        // Delete element
181        lseq.delete(&pos2).unwrap();
182        let elements = lseq.to_vec();
183        assert!(elements.contains(&"hello".to_string()));
184        assert!(!elements.contains(&"world".to_string()));
185        assert!(elements.contains(&"!".to_string()));
186    }
187    
188    #[test]
189    fn test_lseq_merge() {
190        let replica_id1 = create_replica(1);
191        let replica_id2 = create_replica(2);
192        
193        let mut lseq1 = Lseq::<String>::new(replica_id1);
194        let mut lseq2 = Lseq::<String>::new(replica_id2);
195        
196        // Insert different elements
197        lseq1.insert("hello".to_string(), None).unwrap();
198        lseq2.insert("world".to_string(), None).unwrap();
199        
200        // Merge
201        lseq1.merge(&lseq2).unwrap();
202        
203        // Should contain both elements
204        let elements = lseq1.to_vec();
205        assert!(elements.contains(&"hello".to_string()));
206        assert!(elements.contains(&"world".to_string()));
207    }
208}