leptos_sync_core/crdt/
mod.rs

1//! Conflict-free Replicated Data Types (CRDTs) for distributed systems
2//! 
3//! This module provides various CRDT implementations that can be used
4//! to build eventually consistent distributed applications.
5
6pub mod basic;
7pub mod list;
8pub mod tree;
9pub mod graph;
10pub mod builder;
11pub mod advanced;
12
13// Re-export basic CRDTs
14pub use basic::{LwwRegister, LwwMap, GCounter, ReplicaId, Mergeable, CRDT};
15
16pub use list::{
17    ElementId, ElementMetadata, ListElement, ListStrategy, ListConfig,
18    AddWinsList, RemoveWinsList, LwwList,
19};
20
21pub use tree::{
22    NodeId, NodeMetadata, TreeNode, TreeStrategy, TreeConfig,
23    AddWinsTree, RemoveWinsTree,
24};
25
26pub use graph::{
27    VertexId, EdgeId, VertexMetadata, EdgeMetadata, Vertex, Edge,
28    GraphStrategy, GraphConfig, AddWinsGraph, RemoveWinsGraph,
29};
30
31// Re-export builder functionality
32pub use builder::{
33    CrdtBuilder, CrdtBuilderConfig, FieldConfig, CrdtStrategy, 
34    CustomCrdt, GenericCrdtField, CrdtField, BuilderError
35};
36
37// Re-export advanced CRDT types
38pub use advanced::{
39    Rga, RgaElement, Lseq, LseqElement, YjsTree, YjsNode, YjsTreeNode,
40    Dag, DagNode, PositionId, AdvancedCrdtError
41};
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46    use crate::crdt::ReplicaId;
47    use uuid::Uuid;
48
49    fn create_replica(id: u64) -> ReplicaId {
50        ReplicaId::from(Uuid::from_u64_pair(0, id))
51    }
52
53    #[test]
54    fn test_list_crdt_integration() {
55        let replica = create_replica(1);
56        let mut list = AddWinsList::new(replica);
57        
58        let id1 = list.add("item1", 1000);
59        let id2 = list.add("item2", 2000);
60        
61        assert_eq!(list.len(), 2);
62        assert!(list.contains(&id1));
63        assert!(list.contains(&id2));
64    }
65
66    #[test]
67    fn test_tree_crdt_integration() {
68        let replica = create_replica(1);
69        let mut tree = AddWinsTree::new(replica);
70        
71        let root_id = tree.add_root("root", 1000);
72        let child_id = tree.add_child(&root_id, "child", 2000).unwrap();
73        
74        assert_eq!(tree.len(), 2);
75        assert!(tree.contains(&root_id));
76        assert!(tree.contains(&child_id));
77    }
78
79    #[test]
80    fn test_graph_crdt_integration() {
81        let replica = create_replica(1);
82        let mut graph = AddWinsGraph::new(replica);
83        
84        let v1_id = graph.add_vertex("vertex1", 1000);
85        let v2_id = graph.add_vertex("vertex2", 2000);
86        let edge_id = graph.add_edge(&v1_id, &v2_id, 3000, None).unwrap();
87        
88        assert_eq!(graph.vertex_count(), 2);
89        assert_eq!(graph.edge_count(), 1);
90        assert!(graph.contains_vertex(&v1_id));
91        assert!(graph.contains_edge(&edge_id));
92    }
93
94    #[test]
95    fn test_crdt_traits() {
96        let replica = create_replica(1);
97        
98        // Test that all CRDT types implement the required traits
99        let list: AddWinsList<String> = AddWinsList::new(replica);
100        let tree: AddWinsTree<String> = AddWinsTree::new(replica);
101        let graph: AddWinsGraph<String> = AddWinsGraph::new(replica);
102        
103        // This should compile if all types implement CRDT trait
104        let _: &dyn CRDT = &list;
105        let _: &dyn CRDT = &tree;
106        let _: &dyn CRDT = &graph;
107    }
108
109    #[test]
110    fn test_custom_crdt_builder_integration() {
111        let replica = create_replica(1);
112        
113        // Create a custom CRDT using the builder
114        let config = CrdtBuilder::new("UserProfile".to_string())
115            .add_field("name".to_string(), CrdtStrategy::Lww)
116            .add_field("age".to_string(), CrdtStrategy::Lww)
117            .add_field("friends".to_string(), CrdtStrategy::AddWins)
118            .add_optional_field("bio".to_string(), CrdtStrategy::Lww, 
119                serde_json::Value::String("No bio yet".to_string()))
120            .build();
121        
122        let mut profile = CustomCrdt::new(config, replica);
123        
124        // Set field values
125        profile.set_field("name", serde_json::Value::String("Alice".to_string())).unwrap();
126        profile.set_field("age", serde_json::Value::Number(serde_json::Number::from(25))).unwrap();
127        profile.set_field("friends", serde_json::Value::Array(vec![
128            serde_json::Value::String("Bob".to_string()),
129            serde_json::Value::String("Charlie".to_string()),
130        ])).unwrap();
131        
132        // Test field access
133        assert_eq!(profile.get_field("name"), Some(&serde_json::Value::String("Alice".to_string())));
134        assert_eq!(profile.get_field("age"), Some(&serde_json::Value::Number(serde_json::Number::from(25))));
135        assert_eq!(profile.get_field("bio"), Some(&serde_json::Value::String("No bio yet".to_string())));
136        
137        // Test CRDT trait implementation
138        let _: &dyn CRDT = &profile;
139        
140        // Test mergeable trait
141        let mut profile2 = profile.clone();
142        
143        // Small delay to ensure different timestamp for LWW
144        std::thread::sleep(std::time::Duration::from_millis(1));
145        
146        profile2.set_field("name", serde_json::Value::String("Alice Updated".to_string())).unwrap();
147        profile2.set_field("friends", serde_json::Value::Array(vec![
148            serde_json::Value::String("David".to_string()),
149        ])).unwrap();
150        
151        // Merge profiles
152        profile.merge(&profile2).unwrap();
153        
154        // Check merged values
155        assert_eq!(profile.get_field("name"), Some(&serde_json::Value::String("Alice Updated".to_string())));
156        // Friends should be combined (AddWins strategy)
157        if let Some(friends) = profile.get_field("friends") {
158            if let Some(friends_array) = friends.as_array() {
159                assert_eq!(friends_array.len(), 3); // Bob, Charlie, David
160            }
161        }
162    }
163
164    #[test]
165    fn test_advanced_crdt_integration() {
166        let replica1 = create_replica(1);
167        let replica2 = create_replica(2);
168        
169        // Test RGA integration
170        let mut rga1 = Rga::new(replica1.clone());
171        let mut rga2 = Rga::new(replica2.clone());
172        
173        let _pos1 = rga1.insert_after("hello".to_string(), None).unwrap();
174        let _pos2 = rga2.insert_after("world".to_string(), None).unwrap();
175        
176        rga1.merge(&rga2).unwrap();
177        let elements = rga1.to_vec();
178        assert!(elements.contains(&"hello".to_string()));
179        assert!(elements.contains(&"world".to_string()));
180        
181        // Test LSEQ integration
182        let mut lseq1 = Lseq::new(replica1.clone());
183        let mut lseq2 = Lseq::new(replica2.clone());
184        
185        lseq1.insert("item1".to_string(), None).unwrap();
186        lseq2.insert("item2".to_string(), None).unwrap();
187        
188        lseq1.merge(&lseq2).unwrap();
189        let elements = lseq1.to_vec();
190        assert!(elements.contains(&"item1".to_string()));
191        assert!(elements.contains(&"item2".to_string()));
192        
193        // Test Yjs Tree integration
194        let mut tree1 = YjsTree::new(replica1.clone());
195        let mut tree2 = YjsTree::new(replica2.clone());
196        
197        let root1_id = tree1.add_root("root1".to_string()).unwrap();
198        let root2_id = tree2.add_root("root2".to_string()).unwrap();
199        
200        tree1.add_child(&root1_id, "child1".to_string()).unwrap();
201        tree2.add_child(&root2_id, "child2".to_string()).unwrap();
202        
203        tree1.merge(&tree2).unwrap();
204        assert_eq!(tree1.len(), 4); // 2 roots + 2 children
205        
206        // Test DAG integration
207        let mut dag1 = Dag::new(replica1);
208        let mut dag2 = Dag::new(replica2);
209        
210        let node1_id = dag1.add_node("node1".to_string()).unwrap();
211        let node2_id = dag2.add_node("node2".to_string()).unwrap();
212        
213        // No edges needed for merge test
214        
215        dag1.merge(&dag2).unwrap();
216        assert_eq!(dag1.len(), 2);
217    }
218}