Skip to main content

mdcs_db/
json_crdt.rs

1//! JSON CRDT - Automerge-like nested object CRDT.
2//!
3//! Provides collaborative editing of JSON-like documents with:
4//! - Nested objects and arrays
5//! - Path-based operations
6//! - Conflict-free concurrent edits
7//! - Multi-value registers for concurrent writes
8//!
9//! Uses a shared causal context for correct semantics.
10
11use crate::error::DbError;
12use crate::rga_list::{RGAList, RGAListDelta};
13use mdcs_core::lattice::Lattice;
14use serde::{Deserialize, Serialize};
15use std::collections::{HashMap, HashSet};
16use ulid::Ulid;
17
18/// A path into a JSON document.
19#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
20pub struct JsonPath(Vec<PathSegment>);
21
22impl JsonPath {
23    /// Create an empty (root) path.
24    pub fn root() -> Self {
25        Self(Vec::new())
26    }
27
28    /// Create a path from segments.
29    pub fn new(segments: Vec<PathSegment>) -> Self {
30        Self(segments)
31    }
32
33    /// Parse a path from dot notation (e.g., "user.name" or "items.0.value").
34    pub fn parse(path: &str) -> Self {
35        if path.is_empty() {
36            return Self::root();
37        }
38        let segments = path
39            .split('.')
40            .map(|s| {
41                if let Ok(idx) = s.parse::<usize>() {
42                    PathSegment::Index(idx)
43                } else {
44                    PathSegment::Key(s.to_string())
45                }
46            })
47            .collect();
48        Self(segments)
49    }
50
51    /// Get the segments.
52    pub fn segments(&self) -> &[PathSegment] {
53        &self.0
54    }
55
56    /// Check if this is the root path.
57    pub fn is_root(&self) -> bool {
58        self.0.is_empty()
59    }
60
61    /// Get the parent path.
62    pub fn parent(&self) -> Option<Self> {
63        if self.0.is_empty() {
64            None
65        } else {
66            Some(Self(self.0[..self.0.len() - 1].to_vec()))
67        }
68    }
69
70    /// Get the last segment.
71    pub fn last(&self) -> Option<&PathSegment> {
72        self.0.last()
73    }
74
75    /// Append a segment.
76    pub fn push(&mut self, segment: PathSegment) {
77        self.0.push(segment);
78    }
79
80    /// Create a child path with a key.
81    pub fn child_key(&self, key: impl Into<String>) -> Self {
82        let mut new = self.clone();
83        new.push(PathSegment::Key(key.into()));
84        new
85    }
86
87    /// Create a child path with an index.
88    pub fn child_index(&self, index: usize) -> Self {
89        let mut new = self.clone();
90        new.push(PathSegment::Index(index));
91        new
92    }
93}
94
95impl std::fmt::Display for JsonPath {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        let s: Vec<String> = self.0.iter().map(|s| s.to_string()).collect();
98        write!(f, "{}", s.join("."))
99    }
100}
101
102/// A segment in a JSON path.
103#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
104pub enum PathSegment {
105    /// Object key.
106    Key(String),
107    /// Array index.
108    Index(usize),
109}
110
111impl std::fmt::Display for PathSegment {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        match self {
114            PathSegment::Key(k) => write!(f, "{}", k),
115            PathSegment::Index(i) => write!(f, "{}", i),
116        }
117    }
118}
119
120/// A JSON value that can be stored in the CRDT.
121#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]
122pub enum JsonValue {
123    /// Null value.
124    #[default]
125    Null,
126    /// Boolean value.
127    Bool(bool),
128    /// Integer value.
129    Int(i64),
130    /// Floating point value.
131    Float(f64),
132    /// String value.
133    String(String),
134    /// Array reference (points to an RGAList).
135    Array(ArrayId),
136    /// Object reference (points to an ObjectMap).
137    Object(ObjectId),
138}
139
140impl JsonValue {
141    pub fn is_null(&self) -> bool {
142        matches!(self, JsonValue::Null)
143    }
144
145    pub fn as_bool(&self) -> Option<bool> {
146        match self {
147            JsonValue::Bool(b) => Some(*b),
148            _ => None,
149        }
150    }
151
152    pub fn as_int(&self) -> Option<i64> {
153        match self {
154            JsonValue::Int(i) => Some(*i),
155            _ => None,
156        }
157    }
158
159    pub fn as_float(&self) -> Option<f64> {
160        match self {
161            JsonValue::Float(f) => Some(*f),
162            _ => None,
163        }
164    }
165
166    pub fn as_str(&self) -> Option<&str> {
167        match self {
168            JsonValue::String(s) => Some(s),
169            _ => None,
170        }
171    }
172}
173
174/// Unique identifier for an array in the document.
175#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
176pub struct ArrayId(String);
177
178impl ArrayId {
179    pub fn new() -> Self {
180        Self(Ulid::new().to_string())
181    }
182}
183
184impl Default for ArrayId {
185    fn default() -> Self {
186        Self::new()
187    }
188}
189
190/// Unique identifier for an object in the document.
191#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
192pub struct ObjectId(String);
193
194impl ObjectId {
195    pub fn new() -> Self {
196        Self(Ulid::new().to_string())
197    }
198
199    pub fn root() -> Self {
200        Self("root".to_string())
201    }
202}
203
204impl Default for ObjectId {
205    fn default() -> Self {
206        Self::new()
207    }
208}
209
210/// A unique identifier for a field value (for multi-value tracking).
211#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
212pub struct ValueId {
213    replica: String,
214    seq: u64,
215}
216
217impl ValueId {
218    pub fn new(replica: impl Into<String>, seq: u64) -> Self {
219        Self {
220            replica: replica.into(),
221            seq,
222        }
223    }
224}
225
226/// A field in an object that tracks concurrent values.
227#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
228struct ObjectField {
229    /// All concurrent values for this field (multi-value register).
230    values: HashMap<ValueId, JsonValue>,
231    /// Deleted value IDs (tombstones).
232    deleted: HashSet<ValueId>,
233}
234
235impl ObjectField {
236    fn new() -> Self {
237        Self {
238            values: HashMap::new(),
239            deleted: HashSet::new(),
240        }
241    }
242
243    fn set(&mut self, id: ValueId, value: JsonValue) {
244        // Setting a new value obsoletes previous values from this replica
245        let to_delete: Vec<_> = self
246            .values
247            .keys()
248            .filter(|k| k.replica == id.replica)
249            .cloned()
250            .collect();
251        for k in to_delete {
252            self.values.remove(&k);
253        }
254        self.values.insert(id, value);
255    }
256
257    #[allow(dead_code)]
258    fn get(&self) -> Vec<&JsonValue> {
259        self.values.values().collect()
260    }
261
262    fn get_winner(&self) -> Option<&JsonValue> {
263        // Return the value with the highest ValueId (LWW semantics)
264        self.values
265            .iter()
266            .max_by(|(a, _), (b, _)| a.seq.cmp(&b.seq).then_with(|| a.replica.cmp(&b.replica)))
267            .map(|(_, v)| v)
268    }
269
270    fn is_deleted(&self) -> bool {
271        self.values.is_empty() || self.values.values().all(|v| v.is_null())
272    }
273
274    fn merge(&mut self, other: &ObjectField) {
275        for (id, value) in &other.values {
276            if !self.deleted.contains(id) {
277                self.values
278                    .entry(id.clone())
279                    .or_insert_with(|| value.clone());
280            }
281        }
282        self.deleted.extend(other.deleted.iter().cloned());
283        // Remove deleted values
284        for id in &self.deleted {
285            self.values.remove(id);
286        }
287    }
288}
289
290/// An object (map) in the JSON document.
291#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
292struct JsonObject {
293    id: ObjectId,
294    fields: HashMap<String, ObjectField>,
295}
296
297impl JsonObject {
298    fn new(id: ObjectId) -> Self {
299        Self {
300            id,
301            fields: HashMap::new(),
302        }
303    }
304
305    fn set(&mut self, key: String, value_id: ValueId, value: JsonValue) {
306        self.fields
307            .entry(key)
308            .or_insert_with(ObjectField::new)
309            .set(value_id, value);
310    }
311
312    fn get(&self, key: &str) -> Option<&JsonValue> {
313        self.fields.get(key)?.get_winner()
314    }
315
316    #[allow(dead_code)]
317    fn get_all(&self, key: &str) -> Vec<&JsonValue> {
318        self.fields.get(key).map(|f| f.get()).unwrap_or_default()
319    }
320
321    fn keys(&self) -> impl Iterator<Item = &String> + '_ {
322        self.fields
323            .iter()
324            .filter(|(_, f)| !f.is_deleted())
325            .map(|(k, _)| k)
326    }
327
328    fn remove(&mut self, key: &str, value_id: ValueId) {
329        if let Some(field) = self.fields.get_mut(key) {
330            // Mark all existing values as deleted
331            let to_delete: Vec<_> = field.values.keys().cloned().collect();
332            for id in to_delete {
333                field.deleted.insert(id);
334            }
335            field.values.clear();
336            // Set null to record the deletion
337            field.values.insert(value_id, JsonValue::Null);
338        }
339    }
340
341    fn merge(&mut self, other: &JsonObject) {
342        for (key, field) in &other.fields {
343            self.fields
344                .entry(key.clone())
345                .or_insert_with(ObjectField::new)
346                .merge(field);
347        }
348    }
349}
350
351/// An array in the JSON document (using RGAList).
352#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
353struct JsonArray {
354    id: ArrayId,
355    list: RGAList<JsonValue>,
356}
357
358impl JsonArray {
359    fn new(id: ArrayId, replica_id: &str) -> Self {
360        Self {
361            id,
362            list: RGAList::new(replica_id),
363        }
364    }
365
366    #[allow(dead_code)]
367    fn get(&self, index: usize) -> Option<&JsonValue> {
368        self.list.get(index)
369    }
370
371    fn len(&self) -> usize {
372        self.list.len()
373    }
374
375    fn insert(&mut self, index: usize, value: JsonValue) {
376        self.list.insert(index, value);
377    }
378
379    fn remove(&mut self, index: usize) -> Option<JsonValue> {
380        self.list.delete(index)
381    }
382
383    fn push(&mut self, value: JsonValue) {
384        self.list.push_back(value);
385    }
386
387    fn iter(&self) -> impl Iterator<Item = &JsonValue> + '_ {
388        self.list.iter()
389    }
390
391    fn merge(&mut self, other: &JsonArray) {
392        self.list = self.list.join(&other.list);
393    }
394}
395
396/// Delta for JSON CRDT operations.
397#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
398pub struct JsonCrdtDelta {
399    /// Object field changes.
400    pub object_changes: Vec<ObjectChange>,
401    /// Array changes.
402    pub array_changes: Vec<ArrayChange>,
403    /// New objects created.
404    pub new_objects: Vec<ObjectId>,
405    /// New arrays created.
406    pub new_arrays: Vec<ArrayId>,
407}
408
409#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
410pub struct ObjectChange {
411    pub object_id: ObjectId,
412    pub key: String,
413    pub value_id: ValueId,
414    pub value: JsonValue,
415}
416
417#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
418pub struct ArrayChange {
419    pub array_id: ArrayId,
420    pub delta: RGAListDelta<JsonValue>,
421}
422
423impl JsonCrdtDelta {
424    pub fn new() -> Self {
425        Self {
426            object_changes: Vec::new(),
427            array_changes: Vec::new(),
428            new_objects: Vec::new(),
429            new_arrays: Vec::new(),
430        }
431    }
432
433    pub fn is_empty(&self) -> bool {
434        self.object_changes.is_empty()
435            && self.array_changes.is_empty()
436            && self.new_objects.is_empty()
437            && self.new_arrays.is_empty()
438    }
439}
440
441impl Default for JsonCrdtDelta {
442    fn default() -> Self {
443        Self::new()
444    }
445}
446
447/// Collaborative JSON document CRDT.
448///
449/// Provides Automerge-like semantics for editing nested
450/// JSON structures with conflict-free concurrent operations.
451#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
452pub struct JsonCrdt {
453    /// The replica ID.
454    replica_id: String,
455    /// Sequence counter for generating value IDs.
456    seq: u64,
457    /// The root object.
458    root_id: ObjectId,
459    /// All objects in the document.
460    objects: HashMap<ObjectId, JsonObject>,
461    /// All arrays in the document.
462    arrays: HashMap<ArrayId, JsonArray>,
463    /// Pending delta.
464    #[serde(skip)]
465    pending_delta: Option<JsonCrdtDelta>,
466}
467
468impl JsonCrdt {
469    /// Create a new empty JSON document.
470    pub fn new(replica_id: impl Into<String>) -> Self {
471        let replica_id = replica_id.into();
472        let root_id = ObjectId::root();
473        let root = JsonObject::new(root_id.clone());
474
475        let mut objects = HashMap::new();
476        objects.insert(root_id.clone(), root);
477
478        Self {
479            replica_id,
480            seq: 0,
481            root_id,
482            objects,
483            arrays: HashMap::new(),
484            pending_delta: None,
485        }
486    }
487
488    /// Get the replica ID.
489    pub fn replica_id(&self) -> &str {
490        &self.replica_id
491    }
492
493    /// Generate a new value ID.
494    fn next_value_id(&mut self) -> ValueId {
495        self.seq += 1;
496        ValueId::new(&self.replica_id, self.seq)
497    }
498
499    /// Get a value at a path.
500    pub fn get(&self, path: &JsonPath) -> Option<&JsonValue> {
501        let mut current_obj_id = &self.root_id;
502        let segments = path.segments();
503
504        for (i, segment) in segments.iter().enumerate() {
505            let is_last = i == segments.len() - 1;
506
507            match segment {
508                PathSegment::Key(key) => {
509                    let obj = self.objects.get(current_obj_id)?;
510                    let value = obj.get(key)?;
511
512                    if is_last {
513                        return Some(value);
514                    }
515
516                    match value {
517                        JsonValue::Object(id) => current_obj_id = id,
518                        JsonValue::Array(_) if !is_last => {
519                            // Next segment should be an index
520                            continue;
521                        }
522                        _ => return None,
523                    }
524                }
525                PathSegment::Index(_idx) => {
526                    // Need to be at an array
527                    let _obj = self.objects.get(current_obj_id)?;
528                    // Find the array value
529                    // This is a simplification; in practice we'd track which field is the array
530                    return None; // Simplified - would need array traversal
531                }
532            }
533        }
534
535        // Root path returns None - use to_json() instead
536        None
537    }
538
539    /// Set a value at a path.
540    pub fn set(&mut self, path: &JsonPath, value: JsonValue) -> Result<(), DbError> {
541        if path.is_root() {
542            return Err(DbError::InvalidPath("Cannot set root".to_string()));
543        }
544
545        let parent_path = path.parent().unwrap_or(JsonPath::root());
546        let last_segment = path
547            .last()
548            .ok_or_else(|| DbError::InvalidPath("Empty path".to_string()))?;
549
550        // Ensure parent exists and is an object
551        let parent_obj_id = self.ensure_object_at(&parent_path)?;
552
553        let value_id = self.next_value_id();
554
555        match last_segment {
556            PathSegment::Key(key) => {
557                // Handle nested object/array creation
558                let actual_value = match &value {
559                    JsonValue::Object(_) | JsonValue::Array(_) => value,
560                    _ => value,
561                };
562
563                if let Some(obj) = self.objects.get_mut(&parent_obj_id) {
564                    obj.set(key.clone(), value_id.clone(), actual_value.clone());
565                }
566
567                // Record delta
568                let delta = self.pending_delta.get_or_insert_with(JsonCrdtDelta::new);
569                delta.object_changes.push(ObjectChange {
570                    object_id: parent_obj_id,
571                    key: key.clone(),
572                    value_id,
573                    value: actual_value,
574                });
575            }
576            PathSegment::Index(_) => {
577                return Err(DbError::UnsupportedOperation(
578                    "Set by index not supported; use array_insert".to_string(),
579                ));
580            }
581        }
582
583        Ok(())
584    }
585
586    /// Delete a value at a path.
587    pub fn delete(&mut self, path: &JsonPath) -> Result<(), DbError> {
588        if path.is_root() {
589            return Err(DbError::InvalidPath("Cannot delete root".to_string()));
590        }
591
592        let parent_path = path.parent().unwrap_or(JsonPath::root());
593        let last_segment = path
594            .last()
595            .ok_or_else(|| DbError::InvalidPath("Empty path".to_string()))?;
596
597        let parent_obj_id = self
598            .get_object_id_at(&parent_path)
599            .ok_or_else(|| DbError::PathNotFound(parent_path.to_string()))?;
600
601        let value_id = self.next_value_id();
602
603        match last_segment {
604            PathSegment::Key(key) => {
605                if let Some(obj) = self.objects.get_mut(&parent_obj_id) {
606                    obj.remove(key, value_id.clone());
607                }
608
609                // Record delta
610                let delta = self.pending_delta.get_or_insert_with(JsonCrdtDelta::new);
611                delta.object_changes.push(ObjectChange {
612                    object_id: parent_obj_id,
613                    key: key.clone(),
614                    value_id,
615                    value: JsonValue::Null,
616                });
617            }
618            PathSegment::Index(_) => {
619                return Err(DbError::UnsupportedOperation(
620                    "Delete by index not supported; use array_remove".to_string(),
621                ));
622            }
623        }
624
625        Ok(())
626    }
627
628    /// Create a new object and return its ID.
629    pub fn create_object(&mut self) -> ObjectId {
630        let id = ObjectId::new();
631        let obj = JsonObject::new(id.clone());
632        self.objects.insert(id.clone(), obj);
633
634        let delta = self.pending_delta.get_or_insert_with(JsonCrdtDelta::new);
635        delta.new_objects.push(id.clone());
636
637        id
638    }
639
640    /// Create a new array and return its ID.
641    pub fn create_array(&mut self) -> ArrayId {
642        let id = ArrayId::new();
643        let arr = JsonArray::new(id.clone(), &self.replica_id);
644        self.arrays.insert(id.clone(), arr);
645
646        let delta = self.pending_delta.get_or_insert_with(JsonCrdtDelta::new);
647        delta.new_arrays.push(id.clone());
648
649        id
650    }
651
652    /// Set a nested object at a path.
653    pub fn set_object(&mut self, path: &JsonPath) -> Result<ObjectId, DbError> {
654        let obj_id = self.create_object();
655        self.set(path, JsonValue::Object(obj_id.clone()))?;
656        Ok(obj_id)
657    }
658
659    /// Set a nested array at a path.
660    pub fn set_array(&mut self, path: &JsonPath) -> Result<ArrayId, DbError> {
661        let arr_id = self.create_array();
662        self.set(path, JsonValue::Array(arr_id.clone()))?;
663        Ok(arr_id)
664    }
665
666    /// Get an array by ID (internal use).
667    #[allow(dead_code)]
668    fn get_array(&self, id: &ArrayId) -> Option<&JsonArray> {
669        self.arrays.get(id)
670    }
671
672    /// Get a mutable array by ID.
673    #[allow(dead_code)]
674    fn get_array_mut(&mut self, id: &ArrayId) -> Option<&mut JsonArray> {
675        self.arrays.get_mut(id)
676    }
677
678    /// Insert into an array.
679    pub fn array_insert(
680        &mut self,
681        array_id: &ArrayId,
682        index: usize,
683        value: JsonValue,
684    ) -> Result<(), DbError> {
685        let arr = self
686            .arrays
687            .get_mut(array_id)
688            .ok_or_else(|| DbError::PathNotFound(format!("Array {:?}", array_id)))?;
689
690        arr.insert(index, value);
691
692        if let Some(delta) = arr.list.take_delta() {
693            let doc_delta = self.pending_delta.get_or_insert_with(JsonCrdtDelta::new);
694            doc_delta.array_changes.push(ArrayChange {
695                array_id: array_id.clone(),
696                delta,
697            });
698        }
699
700        Ok(())
701    }
702
703    /// Push to an array.
704    pub fn array_push(&mut self, array_id: &ArrayId, value: JsonValue) -> Result<(), DbError> {
705        let arr = self
706            .arrays
707            .get_mut(array_id)
708            .ok_or_else(|| DbError::PathNotFound(format!("Array {:?}", array_id)))?;
709
710        arr.push(value);
711
712        if let Some(delta) = arr.list.take_delta() {
713            let doc_delta = self.pending_delta.get_or_insert_with(JsonCrdtDelta::new);
714            doc_delta.array_changes.push(ArrayChange {
715                array_id: array_id.clone(),
716                delta,
717            });
718        }
719
720        Ok(())
721    }
722
723    /// Remove from an array.
724    pub fn array_remove(&mut self, array_id: &ArrayId, index: usize) -> Result<JsonValue, DbError> {
725        let arr = self
726            .arrays
727            .get_mut(array_id)
728            .ok_or_else(|| DbError::PathNotFound(format!("Array {:?}", array_id)))?;
729
730        let arr_len = arr.len();
731        let value = arr.remove(index).ok_or(DbError::IndexOutOfBounds {
732            index,
733            length: arr_len,
734        })?;
735
736        if let Some(delta) = arr.list.take_delta() {
737            let doc_delta = self.pending_delta.get_or_insert_with(JsonCrdtDelta::new);
738            doc_delta.array_changes.push(ArrayChange {
739                array_id: array_id.clone(),
740                delta,
741            });
742        }
743
744        Ok(value)
745    }
746
747    /// Get array length.
748    pub fn array_len(&self, array_id: &ArrayId) -> Option<usize> {
749        self.arrays.get(array_id).map(|a| a.len())
750    }
751
752    /// Get all keys in the root object.
753    pub fn keys(&self) -> Vec<String> {
754        self.objects
755            .get(&self.root_id)
756            .map(|obj| obj.keys().cloned().collect())
757            .unwrap_or_default()
758    }
759
760    /// Check if a key exists in the root object.
761    pub fn contains_key(&self, key: &str) -> bool {
762        self.objects
763            .get(&self.root_id)
764            .map(|obj| obj.get(key).is_some())
765            .unwrap_or(false)
766    }
767
768    // === Helper Methods ===
769
770    fn get_object_id_at(&self, path: &JsonPath) -> Option<ObjectId> {
771        if path.is_root() {
772            return Some(self.root_id.clone());
773        }
774
775        let value = self.get(path)?;
776        match value {
777            JsonValue::Object(id) => Some(id.clone()),
778            _ => None,
779        }
780    }
781
782    fn ensure_object_at(&mut self, path: &JsonPath) -> Result<ObjectId, DbError> {
783        if path.is_root() {
784            return Ok(self.root_id.clone());
785        }
786
787        // Try to get existing
788        if let Some(id) = self.get_object_id_at(path) {
789            return Ok(id);
790        }
791
792        // Need to create
793        self.set_object(path)
794    }
795
796    // === Delta Operations ===
797
798    /// Take the pending delta.
799    pub fn take_delta(&mut self) -> Option<JsonCrdtDelta> {
800        self.pending_delta.take()
801    }
802
803    /// Apply a delta from another replica.
804    pub fn apply_delta(&mut self, delta: &JsonCrdtDelta) {
805        // Create new objects
806        for obj_id in &delta.new_objects {
807            self.objects
808                .entry(obj_id.clone())
809                .or_insert_with(|| JsonObject::new(obj_id.clone()));
810        }
811
812        // Create new arrays
813        for arr_id in &delta.new_arrays {
814            self.arrays
815                .entry(arr_id.clone())
816                .or_insert_with(|| JsonArray::new(arr_id.clone(), &self.replica_id));
817        }
818
819        // Apply object changes
820        for change in &delta.object_changes {
821            if let Some(obj) = self.objects.get_mut(&change.object_id) {
822                obj.set(
823                    change.key.clone(),
824                    change.value_id.clone(),
825                    change.value.clone(),
826                );
827            }
828        }
829
830        // Apply array changes
831        for change in &delta.array_changes {
832            if let Some(arr) = self.arrays.get_mut(&change.array_id) {
833                arr.list.apply_delta(&change.delta);
834            }
835        }
836    }
837
838    // === Conversion ===
839
840    /// Convert to a serde_json::Value.
841    pub fn to_json(&self) -> serde_json::Value {
842        self.object_to_json(&self.root_id)
843    }
844
845    fn object_to_json(&self, obj_id: &ObjectId) -> serde_json::Value {
846        let obj = match self.objects.get(obj_id) {
847            Some(o) => o,
848            None => return serde_json::Value::Null,
849        };
850
851        let mut map = serde_json::Map::new();
852        for key in obj.keys() {
853            if let Some(value) = obj.get(key) {
854                map.insert(key.clone(), self.value_to_json(value));
855            }
856        }
857        serde_json::Value::Object(map)
858    }
859
860    fn array_to_json(&self, arr_id: &ArrayId) -> serde_json::Value {
861        let arr = match self.arrays.get(arr_id) {
862            Some(a) => a,
863            None => return serde_json::Value::Array(vec![]),
864        };
865
866        let values: Vec<_> = arr.iter().map(|v| self.value_to_json(v)).collect();
867        serde_json::Value::Array(values)
868    }
869
870    fn value_to_json(&self, value: &JsonValue) -> serde_json::Value {
871        match value {
872            JsonValue::Null => serde_json::Value::Null,
873            JsonValue::Bool(b) => serde_json::Value::Bool(*b),
874            JsonValue::Int(i) => serde_json::Value::Number((*i).into()),
875            JsonValue::Float(f) => serde_json::Number::from_f64(*f)
876                .map(serde_json::Value::Number)
877                .unwrap_or(serde_json::Value::Null),
878            JsonValue::String(s) => serde_json::Value::String(s.clone()),
879            JsonValue::Object(id) => self.object_to_json(id),
880            JsonValue::Array(id) => self.array_to_json(id),
881        }
882    }
883}
884
885impl Lattice for JsonCrdt {
886    fn bottom() -> Self {
887        Self::new("")
888    }
889
890    fn join(&self, other: &Self) -> Self {
891        let mut result = self.clone();
892
893        // Merge objects
894        for (id, other_obj) in &other.objects {
895            result
896                .objects
897                .entry(id.clone())
898                .and_modify(|obj| obj.merge(other_obj))
899                .or_insert_with(|| other_obj.clone());
900        }
901
902        // Merge arrays
903        for (id, other_arr) in &other.arrays {
904            result
905                .arrays
906                .entry(id.clone())
907                .and_modify(|arr| arr.merge(other_arr))
908                .or_insert_with(|| other_arr.clone());
909        }
910
911        result
912    }
913}
914
915impl Default for JsonCrdt {
916    fn default() -> Self {
917        Self::new("")
918    }
919}
920
921#[cfg(test)]
922mod tests {
923    use super::*;
924
925    #[test]
926    fn test_basic_set_get() {
927        let mut doc = JsonCrdt::new("r1");
928
929        doc.set(
930            &JsonPath::parse("name"),
931            JsonValue::String("Alice".to_string()),
932        )
933        .unwrap();
934        doc.set(&JsonPath::parse("age"), JsonValue::Int(30))
935            .unwrap();
936
937        let name = doc.get(&JsonPath::parse("name")).unwrap();
938        assert_eq!(name.as_str(), Some("Alice"));
939
940        let age = doc.get(&JsonPath::parse("age")).unwrap();
941        assert_eq!(age.as_int(), Some(30));
942    }
943
944    #[test]
945    fn test_nested_object() {
946        let mut doc = JsonCrdt::new("r1");
947
948        let _user_id = doc.set_object(&JsonPath::parse("user")).unwrap();
949        doc.set(
950            &JsonPath::parse("user.name"),
951            JsonValue::String("Bob".to_string()),
952        )
953        .unwrap();
954
955        assert!(doc.contains_key("user"));
956
957        // The path-based get for nested objects needs the value to be Object type
958        let user_value = doc.get(&JsonPath::parse("user"));
959        assert!(user_value.is_some());
960    }
961
962    #[test]
963    fn test_array_operations() {
964        let mut doc = JsonCrdt::new("r1");
965
966        let arr_id = doc.create_array();
967        doc.set(&JsonPath::parse("items"), JsonValue::Array(arr_id.clone()))
968            .unwrap();
969
970        doc.array_push(&arr_id, JsonValue::String("one".to_string()))
971            .unwrap();
972        doc.array_push(&arr_id, JsonValue::String("two".to_string()))
973            .unwrap();
974        doc.array_push(&arr_id, JsonValue::String("three".to_string()))
975            .unwrap();
976
977        assert_eq!(doc.array_len(&arr_id), Some(3));
978
979        let removed = doc.array_remove(&arr_id, 1).unwrap();
980        assert_eq!(removed.as_str(), Some("two"));
981        assert_eq!(doc.array_len(&arr_id), Some(2));
982    }
983
984    #[test]
985    fn test_delete() {
986        let mut doc = JsonCrdt::new("r1");
987
988        doc.set(
989            &JsonPath::parse("temp"),
990            JsonValue::String("value".to_string()),
991        )
992        .unwrap();
993        assert!(doc.contains_key("temp"));
994
995        doc.delete(&JsonPath::parse("temp")).unwrap();
996        // After delete, the key may still exist but with null value
997    }
998
999    #[test]
1000    fn test_concurrent_sets() {
1001        let mut doc1 = JsonCrdt::new("r1");
1002        let mut doc2 = JsonCrdt::new("r2");
1003
1004        // Both set same key concurrently
1005        doc1.set(
1006            &JsonPath::parse("value"),
1007            JsonValue::String("from_r1".to_string()),
1008        )
1009        .unwrap();
1010        doc2.set(
1011            &JsonPath::parse("value"),
1012            JsonValue::String("from_r2".to_string()),
1013        )
1014        .unwrap();
1015
1016        // Exchange deltas
1017        let delta1 = doc1.take_delta().unwrap();
1018        let delta2 = doc2.take_delta().unwrap();
1019
1020        doc1.apply_delta(&delta2);
1021        doc2.apply_delta(&delta1);
1022
1023        // Both should converge to same value (LWW by replica+seq)
1024        let json1 = doc1.to_json();
1025        let json2 = doc2.to_json();
1026        assert_eq!(json1, json2);
1027    }
1028
1029    #[test]
1030    fn test_to_json() {
1031        let mut doc = JsonCrdt::new("r1");
1032
1033        doc.set(
1034            &JsonPath::parse("name"),
1035            JsonValue::String("Test".to_string()),
1036        )
1037        .unwrap();
1038        doc.set(&JsonPath::parse("count"), JsonValue::Int(42))
1039            .unwrap();
1040        doc.set(&JsonPath::parse("active"), JsonValue::Bool(true))
1041            .unwrap();
1042
1043        let json = doc.to_json();
1044        assert!(json.is_object());
1045        assert_eq!(json["name"], "Test");
1046        assert_eq!(json["count"], 42);
1047        assert_eq!(json["active"], true);
1048    }
1049
1050    #[test]
1051    fn test_path_parsing() {
1052        let path = JsonPath::parse("user.profile.name");
1053        assert_eq!(path.segments().len(), 3);
1054
1055        let path_with_index = JsonPath::parse("items.0.value");
1056        assert_eq!(path_with_index.segments().len(), 3);
1057        assert!(matches!(
1058            path_with_index.segments()[1],
1059            PathSegment::Index(0)
1060        ));
1061    }
1062
1063    #[test]
1064    fn test_lattice_join() {
1065        let mut doc1 = JsonCrdt::new("r1");
1066        let mut doc2 = JsonCrdt::new("r2");
1067
1068        doc1.set(
1069            &JsonPath::parse("a"),
1070            JsonValue::String("from_r1".to_string()),
1071        )
1072        .unwrap();
1073        doc2.set(
1074            &JsonPath::parse("b"),
1075            JsonValue::String("from_r2".to_string()),
1076        )
1077        .unwrap();
1078
1079        let merged = doc1.join(&doc2);
1080
1081        // Should have both keys
1082        assert!(merged.contains_key("a"));
1083        assert!(merged.contains_key("b"));
1084    }
1085
1086    #[test]
1087    fn test_keys() {
1088        let mut doc = JsonCrdt::new("r1");
1089
1090        doc.set(&JsonPath::parse("x"), JsonValue::Int(1)).unwrap();
1091        doc.set(&JsonPath::parse("y"), JsonValue::Int(2)).unwrap();
1092        doc.set(&JsonPath::parse("z"), JsonValue::Int(3)).unwrap();
1093
1094        let keys = doc.keys();
1095        assert_eq!(keys.len(), 3);
1096        assert!(keys.contains(&"x".to_string()));
1097        assert!(keys.contains(&"y".to_string()));
1098        assert!(keys.contains(&"z".to_string()));
1099    }
1100}