Skip to main content

fionn_ops/
dson_traits.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! DSON Trait Abstractions
3//!
4//! This module provides trait-based abstractions for DSON operations,
5//! enabling comparison across implementations and CRDT/merge semantics.
6
7use crate::{
8    DsonOperation, FilterPredicate, MergeStrategy, OperationValue, ReduceFunction, StreamGenerator,
9    TransformFunction,
10};
11use fionn_core::Result;
12use smallvec::SmallVec;
13
14// =============================================================================
15// Core DSON Traits
16// =============================================================================
17
18/// Core document processor trait - the fundamental DSON interface
19pub trait DocumentProcessor {
20    /// Process input JSON and return transformed output
21    ///
22    /// # Errors
23    /// Returns an error if parsing or transformation fails
24    fn process(&mut self, input: &str) -> Result<String>;
25
26    /// Apply a single operation
27    ///
28    /// # Errors
29    /// Returns an error if the operation cannot be applied
30    fn apply_operation(&mut self, op: &DsonOperation) -> Result<()>;
31
32    /// Apply a batch of operations
33    ///
34    /// # Errors
35    /// Returns an error if any operation in the batch fails
36    fn apply_operations(&mut self, ops: &[DsonOperation]) -> Result<()> {
37        for op in ops {
38            self.apply_operation(op)?;
39        }
40        Ok(())
41    }
42
43    /// Generate output from current state
44    ///
45    /// # Errors
46    /// Returns an error if serialization fails
47    fn output(&self) -> Result<String>;
48}
49
50/// Schema-aware filtering trait
51pub trait SchemaAware {
52    /// Check if a path matches the input schema
53    fn matches_input_schema(&self, path: &str) -> bool;
54
55    /// Check if a path matches the output schema
56    fn matches_output_schema(&self, path: &str) -> bool;
57
58    /// Get input schema paths
59    fn input_schema(&self) -> Vec<String>;
60
61    /// Get output schema paths
62    fn output_schema(&self) -> Vec<String>;
63}
64
65/// Field operations trait - CRUD on document fields
66pub trait FieldOperations {
67    /// Add a field at path with value
68    ///
69    /// # Errors
70    /// Returns an error if the field cannot be added at the specified path
71    fn field_add(&mut self, path: &str, value: OperationValue) -> Result<()>;
72
73    /// Modify existing field at path
74    ///
75    /// # Errors
76    /// Returns an error if the field does not exist or cannot be modified
77    fn field_modify(&mut self, path: &str, value: OperationValue) -> Result<()>;
78
79    /// Delete field at path
80    ///
81    /// # Errors
82    /// Returns an error if the field does not exist or cannot be deleted
83    fn field_delete(&mut self, path: &str) -> Result<()>;
84
85    /// Read field value at path
86    ///
87    /// # Errors
88    /// Returns an error if the path is invalid
89    fn field_read(&self, path: &str) -> Result<Option<OperationValue>>;
90
91    /// Check if field exists at path
92    fn field_exists(&self, path: &str) -> bool;
93}
94
95/// Array operations trait - operations on JSON arrays
96pub trait ArrayOperations {
97    /// Insert value at index in array at path
98    ///
99    /// # Errors
100    /// Returns an error if the path is not an array or index is out of bounds
101    fn array_insert(&mut self, path: &str, index: usize, value: OperationValue) -> Result<()>;
102
103    /// Remove element at index from array at path
104    ///
105    /// # Errors
106    /// Returns an error if the path is not an array or index is out of bounds
107    fn array_remove(&mut self, path: &str, index: usize) -> Result<()>;
108
109    /// Replace element at index in array at path
110    ///
111    /// # Errors
112    /// Returns an error if the path is not an array or index is out of bounds
113    fn array_replace(&mut self, path: &str, index: usize, value: OperationValue) -> Result<()>;
114
115    /// Get array length at path
116    ///
117    /// # Errors
118    /// Returns an error if the path is not an array
119    fn array_len(&self, path: &str) -> Result<usize>;
120
121    /// Build array from elements
122    ///
123    /// # Errors
124    /// Returns an error if the array cannot be created at the path
125    fn array_build(&mut self, path: &str, elements: Vec<OperationValue>) -> Result<()>;
126
127    /// Filter array elements
128    ///
129    /// # Errors
130    /// Returns an error if the path is not an array or predicate fails
131    fn array_filter(&mut self, path: &str, predicate: &FilterPredicate) -> Result<()>;
132
133    /// Map over array elements
134    ///
135    /// # Errors
136    /// Returns an error if the path is not an array or transform fails
137    fn array_map(&mut self, path: &str, transform: &TransformFunction) -> Result<()>;
138
139    /// Reduce array to single value
140    ///
141    /// # Errors
142    /// Returns an error if the path is not an array or reducer fails
143    fn array_reduce(
144        &mut self,
145        path: &str,
146        initial: OperationValue,
147        reducer: &ReduceFunction,
148    ) -> Result<OperationValue>;
149}
150
151// =============================================================================
152// CRDT/Merge Traits - The key abstraction for comparing implementations
153// =============================================================================
154
155/// Vector clock for causality tracking
156/// Uses `SmallVec` for common case (< 8 replicas) to avoid heap allocation
157#[derive(Debug, Clone, PartialEq, Eq, Default)]
158pub struct VectorClock {
159    /// Inline storage for small replica counts (most common case)
160    /// Format: (`replica_id`, timestamp) pairs
161    inline: SmallVec<[(u64, u64); 8]>, // (replica_id_hash, timestamp)
162    /// Replica IDs for iteration (only when needed)
163    replica_ids: SmallVec<[String; 8]>,
164}
165
166impl VectorClock {
167    /// Create a new empty vector clock.
168    #[inline]
169    #[must_use]
170    pub fn new() -> Self {
171        Self {
172            inline: SmallVec::new(),
173            replica_ids: SmallVec::new(),
174        }
175    }
176
177    #[inline]
178    fn hash_replica_id(replica_id: &str) -> u64 {
179        use std::hash::{Hash, Hasher};
180        let mut hasher = ahash::AHasher::default();
181        replica_id.hash(&mut hasher);
182        hasher.finish()
183    }
184
185    /// Increment the clock for a replica.
186    #[inline]
187    pub fn increment(&mut self, replica_id: &str) {
188        let hash = Self::hash_replica_id(replica_id);
189        // Linear scan for small vectors is faster than hash lookup
190        for (h, ts) in &mut self.inline {
191            if *h == hash {
192                *ts += 1;
193                return;
194            }
195        }
196        // Not found, add new entry
197        self.inline.push((hash, 1));
198        self.replica_ids.push(replica_id.to_string());
199    }
200
201    /// Get the clock value for a replica.
202    #[inline]
203    #[must_use]
204    pub fn get(&self, replica_id: &str) -> u64 {
205        let hash = Self::hash_replica_id(replica_id);
206        for &(h, ts) in &self.inline {
207            if h == hash {
208                return ts;
209            }
210        }
211        0
212    }
213
214    /// Merge another vector clock into this one.
215    #[inline]
216    pub fn merge(&mut self, other: &Self) {
217        for (i, &(hash, time)) in other.inline.iter().enumerate() {
218            let mut found = false;
219            for (h, ts) in &mut self.inline {
220                if *h == hash {
221                    *ts = (*ts).max(time);
222                    found = true;
223                    break;
224                }
225            }
226            if !found {
227                self.inline.push((hash, time));
228                if i < other.replica_ids.len() {
229                    self.replica_ids.push(other.replica_ids[i].clone());
230                }
231            }
232        }
233    }
234
235    /// Returns true if self happened-before other
236    #[inline]
237    #[must_use]
238    pub fn happened_before(&self, other: &Self) -> bool {
239        let mut dominated = false;
240        for &(hash, time) in &self.inline {
241            let other_time = other.get_by_hash(hash);
242            if time > other_time {
243                return false;
244            }
245            if time < other_time {
246                dominated = true;
247            }
248        }
249        for &(hash, time) in &other.inline {
250            if self.get_by_hash(hash) == 0 && time > 0 {
251                dominated = true;
252            }
253        }
254        dominated
255    }
256
257    #[inline]
258    fn get_by_hash(&self, hash: u64) -> u64 {
259        for &(h, ts) in &self.inline {
260            if h == hash {
261                return ts;
262            }
263        }
264        0
265    }
266
267    /// Returns true if clocks are concurrent (neither happened-before the other)
268    #[inline]
269    #[must_use]
270    pub fn concurrent_with(&self, other: &Self) -> bool {
271        !self.happened_before(other) && !other.happened_before(self)
272    }
273
274    /// For compatibility: get clocks as `BTreeMap` (for iteration in tests/benchmarks)
275    #[must_use]
276    pub fn clocks(&self) -> std::collections::BTreeMap<String, u64> {
277        let mut map = std::collections::BTreeMap::new();
278        for (i, &(_, ts)) in self.inline.iter().enumerate() {
279            if i < self.replica_ids.len() {
280                map.insert(self.replica_ids[i].clone(), ts);
281            }
282        }
283        map
284    }
285}
286
287/// CRDT operation with causal metadata
288#[derive(Debug, Clone)]
289pub struct CrdtOperation {
290    /// The underlying DSON operation
291    pub operation: DsonOperation,
292    /// Lamport timestamp for ordering
293    pub timestamp: u64,
294    /// Replica ID that generated this operation
295    pub replica_id: String,
296    /// Vector clock for causality
297    pub vector_clock: VectorClock,
298}
299
300/// Conflict information when merging concurrent operations
301#[derive(Debug, Clone)]
302pub struct MergeConflict {
303    /// Path where conflict occurred
304    pub path: String,
305    /// Local value at conflict path
306    pub local_value: OperationValue,
307    /// Remote value at conflict path
308    pub remote_value: OperationValue,
309    /// Local operation timestamp
310    pub local_timestamp: u64,
311    /// Remote operation timestamp
312    pub remote_timestamp: u64,
313    /// Resolved value after conflict resolution
314    pub resolved_value: Option<OperationValue>,
315}
316
317/// CRDT merge trait - defines how documents merge across replicas
318pub trait CrdtMerge {
319    /// Merge a remote operation into local state
320    ///
321    /// # Errors
322    /// Returns an error if the merge operation fails
323    fn merge_operation(&mut self, op: CrdtOperation) -> Result<Option<MergeConflict>>;
324
325    /// Merge field with CRDT semantics
326    ///
327    /// # Errors
328    /// Returns an error if the field merge fails
329    fn merge_field(
330        &mut self,
331        path: &str,
332        value: OperationValue,
333        timestamp: u64,
334        strategy: &MergeStrategy,
335    ) -> Result<Option<MergeConflict>>;
336
337    /// Get the current vector clock
338    fn vector_clock(&self) -> &VectorClock;
339
340    /// Get the replica ID
341    fn replica_id(&self) -> &str;
342
343    /// Resolve a conflict using the specified strategy
344    ///
345    /// # Errors
346    /// Returns an error if conflict resolution fails
347    fn resolve_conflict(
348        &mut self,
349        conflict: &MergeConflict,
350        strategy: &MergeStrategy,
351    ) -> Result<OperationValue>;
352}
353
354/// Delta-state CRDT trait for efficient synchronization
355pub trait DeltaCrdt: CrdtMerge {
356    /// Type representing a delta (difference from previous state)
357    type Delta;
358
359    /// Generate delta since given vector clock
360    fn generate_delta(&self, since: &VectorClock) -> Self::Delta;
361
362    /// Apply delta from remote replica
363    ///
364    /// # Errors
365    /// Returns an error if delta application fails
366    fn apply_delta(&mut self, delta: Self::Delta) -> Result<Vec<MergeConflict>>;
367
368    /// Compact/garbage collect old deltas
369    fn compact(&mut self);
370}
371
372/// Operation-based CRDT trait
373pub trait OpBasedCrdt: CrdtMerge {
374    /// Prepare an operation for broadcast (downstream precondition)
375    ///
376    /// # Errors
377    /// Returns an error if operation preparation fails
378    fn prepare(&self, op: &DsonOperation) -> Result<CrdtOperation>;
379
380    /// Effect an operation (apply after delivery)
381    ///
382    /// # Errors
383    /// Returns an error if operation effect fails
384    fn effect(&mut self, op: CrdtOperation) -> Result<Option<MergeConflict>>;
385
386    /// Check if operation is causally ready to be applied
387    fn is_causally_ready(&self, op: &CrdtOperation) -> bool;
388
389    /// Buffer operation until causally ready
390    fn buffer_operation(&mut self, op: CrdtOperation);
391
392    /// Process buffered operations that are now ready
393    ///
394    /// # Errors
395    /// Returns an error if processing buffered operations fails
396    fn process_buffered(&mut self) -> Result<Vec<MergeConflict>>;
397}
398
399// =============================================================================
400// Streaming Traits
401// =============================================================================
402
403/// Streaming processor trait for large datasets
404pub trait StreamProcessor {
405    /// Process a stream of JSON lines
406    ///
407    /// # Errors
408    /// Returns an error if stream processing fails
409    fn process_stream<I>(&mut self, lines: I) -> Result<Vec<String>>
410    where
411        I: Iterator<Item = String>;
412
413    /// Build a stream from generator
414    ///
415    /// # Errors
416    /// Returns an error if stream building fails
417    fn stream_build(&mut self, path: &str, generator: &StreamGenerator) -> Result<()>;
418
419    /// Filter stream elements
420    ///
421    /// # Errors
422    /// Returns an error if stream filtering fails
423    fn stream_filter(&mut self, path: &str, predicate: &FilterPredicate) -> Result<()>;
424
425    /// Map over stream elements
426    ///
427    /// # Errors
428    /// Returns an error if stream mapping fails
429    fn stream_map(&mut self, path: &str, transform: &TransformFunction) -> Result<()>;
430
431    /// Emit batch from stream
432    ///
433    /// # Errors
434    /// Returns an error if batch emission fails
435    fn stream_emit(&mut self, path: &str, batch_size: usize) -> Result<Vec<OperationValue>>;
436}
437
438/// Tape-level processor trait (SIMD-specific)
439pub trait TapeProcessor {
440    /// Type of tape nodes
441    type Node;
442
443    /// Parse JSON into tape representation
444    ///
445    /// # Errors
446    /// Returns an error if JSON parsing fails
447    fn parse(json: &str) -> Result<Self>
448    where
449        Self: Sized;
450
451    /// Get tape nodes
452    fn nodes(&self) -> &[Self::Node];
453
454    /// Serialize tape back to JSON
455    ///
456    /// # Errors
457    /// Returns an error if serialization fails
458    fn serialize(&self) -> Result<String>;
459
460    /// Navigate to path and return node index
461    ///
462    /// # Errors
463    /// Returns an error if path resolution fails
464    fn resolve_path(&self, path: &str) -> Result<Option<usize>>;
465
466    /// Skip value at index, return next index
467    ///
468    /// # Errors
469    /// Returns an error if the index is invalid
470    fn skip_value(&self, index: usize) -> Result<usize>;
471}
472
473// =============================================================================
474// Canonical Operations Trait
475// =============================================================================
476
477/// Canonical operation processor trait
478pub trait CanonicalProcessor {
479    /// Add operation to canonical sequence
480    fn add_operation(&mut self, op: DsonOperation);
481
482    /// Compute canonical (optimized) operation sequence
483    ///
484    /// # Errors
485    /// Returns an error if canonical computation fails
486    fn compute_canonical(&mut self) -> Result<Vec<DsonOperation>>;
487
488    /// Check if operations can be coalesced
489    fn can_coalesce(&self, a: &DsonOperation, b: &DsonOperation) -> bool;
490
491    /// Reorder operations for efficiency
492    fn reorder(&mut self);
493}
494
495// =============================================================================
496// Comparison Framework
497// =============================================================================
498
499/// Trait for comparing DSON implementations
500pub trait DsonImplementation:
501    DocumentProcessor + FieldOperations + ArrayOperations + SchemaAware
502{
503    /// Implementation name for comparison
504    fn name(&self) -> &str;
505
506    /// Implementation version
507    fn version(&self) -> &str;
508
509    /// Supported features
510    fn features(&self) -> Vec<&str>;
511
512    /// Performance characteristics
513    fn characteristics(&self) -> ImplementationCharacteristics;
514}
515
516/// Performance and capability characteristics
517#[derive(Debug, Clone, Default)]
518#[allow(clippy::struct_excessive_bools)] // These are independent feature flags
519pub struct ImplementationCharacteristics {
520    /// Zero-copy parsing support
521    pub zero_copy: bool,
522    /// SIMD acceleration support
523    pub simd_accelerated: bool,
524    /// Streaming support
525    pub streaming: bool,
526    /// CRDT/merge support
527    pub crdt_support: bool,
528    /// Schema filtering support
529    pub schema_filtering: bool,
530    /// Parallel processing support
531    pub parallel: bool,
532    /// Estimated memory overhead per document (bytes)
533    pub memory_overhead: usize,
534    /// Maximum recommended document size (bytes)
535    pub max_document_size: usize,
536}
537
538// =============================================================================
539// Merge Strategy Implementations
540// =============================================================================
541
542impl MergeStrategy {
543    /// Resolve conflict between two values using this strategy
544    #[inline]
545    #[must_use]
546    pub fn resolve(
547        &self,
548        local: &OperationValue,
549        remote: &OperationValue,
550        local_ts: u64,
551        remote_ts: u64,
552    ) -> OperationValue {
553        match self {
554            Self::LastWriteWins => {
555                if remote_ts > local_ts {
556                    remote.clone()
557                } else {
558                    local.clone()
559                }
560            }
561            Self::Max => {
562                // For numeric values, take the max
563                match (local, remote) {
564                    (OperationValue::NumberRef(a), OperationValue::NumberRef(b)) => {
565                        let a_val: f64 = a.parse().unwrap_or(0.0);
566                        let b_val: f64 = b.parse().unwrap_or(0.0);
567                        if b_val > a_val {
568                            remote.clone()
569                        } else {
570                            local.clone()
571                        }
572                    }
573                    _ => {
574                        // Fall back to LWW for non-numeric
575                        if remote_ts > local_ts {
576                            remote.clone()
577                        } else {
578                            local.clone()
579                        }
580                    }
581                }
582            }
583            Self::Min => match (local, remote) {
584                (OperationValue::NumberRef(a), OperationValue::NumberRef(b)) => {
585                    let a_val: f64 = a.parse().unwrap_or(0.0);
586                    let b_val: f64 = b.parse().unwrap_or(0.0);
587                    if b_val < a_val {
588                        remote.clone()
589                    } else {
590                        local.clone()
591                    }
592                }
593                _ => {
594                    if remote_ts > local_ts {
595                        remote.clone()
596                    } else {
597                        local.clone()
598                    }
599                }
600            },
601            Self::Additive => {
602                // For numeric values, sum them
603                match (local, remote) {
604                    (OperationValue::NumberRef(a), OperationValue::NumberRef(b)) => {
605                        let a_val: f64 = a.parse().unwrap_or(0.0);
606                        let b_val: f64 = b.parse().unwrap_or(0.0);
607                        OperationValue::NumberRef((a_val + b_val).to_string())
608                    }
609                    // For strings, concatenate
610                    (OperationValue::StringRef(a), OperationValue::StringRef(b)) => {
611                        OperationValue::StringRef(format!("{a}{b}"))
612                    }
613                    _ => {
614                        if remote_ts > local_ts {
615                            remote.clone()
616                        } else {
617                            local.clone()
618                        }
619                    }
620                }
621            }
622            Self::Union => {
623                // Union only makes sense for arrays/sets - fall back to LWW for scalars
624                if remote_ts > local_ts {
625                    remote.clone()
626                } else {
627                    local.clone()
628                }
629            }
630            Self::Custom(_name) => {
631                // Custom strategies would need a registry - fall back to LWW
632                if remote_ts > local_ts {
633                    remote.clone()
634                } else {
635                    local.clone()
636                }
637            }
638        }
639    }
640}
641
642// =============================================================================
643// Tests
644// =============================================================================
645
646#[cfg(test)]
647mod tests {
648    use super::*;
649
650    #[test]
651    fn test_vector_clock_basics() {
652        let mut vc1 = VectorClock::new();
653        vc1.increment("replica_a");
654        vc1.increment("replica_a");
655
656        let mut vc2 = VectorClock::new();
657        vc2.increment("replica_b");
658
659        assert_eq!(vc1.get("replica_a"), 2);
660        assert_eq!(vc1.get("replica_b"), 0);
661        assert_eq!(vc2.get("replica_b"), 1);
662
663        // Neither happened before the other (concurrent)
664        assert!(vc1.concurrent_with(&vc2));
665    }
666
667    #[test]
668    fn test_vector_clock_causality() {
669        let mut vc1 = VectorClock::new();
670        vc1.increment("replica_a");
671
672        let mut vc2 = vc1.clone();
673        vc2.increment("replica_a");
674
675        // vc1 happened before vc2
676        assert!(vc1.happened_before(&vc2));
677        assert!(!vc2.happened_before(&vc1));
678    }
679
680    #[test]
681    fn test_merge_strategy_lww() {
682        let local = OperationValue::StringRef("local".to_string());
683        let remote = OperationValue::StringRef("remote".to_string());
684
685        let result = MergeStrategy::LastWriteWins.resolve(&local, &remote, 1, 2);
686        assert_eq!(result, remote);
687
688        let result = MergeStrategy::LastWriteWins.resolve(&local, &remote, 2, 1);
689        assert_eq!(result, local);
690    }
691
692    #[test]
693    fn test_merge_strategy_max() {
694        let local = OperationValue::NumberRef("10".to_string());
695        let remote = OperationValue::NumberRef("20".to_string());
696
697        let result = MergeStrategy::Max.resolve(&local, &remote, 1, 1);
698        assert_eq!(result, remote);
699    }
700
701    #[test]
702    fn test_merge_strategy_additive() {
703        let local = OperationValue::NumberRef("10".to_string());
704        let remote = OperationValue::NumberRef("20".to_string());
705
706        let result = MergeStrategy::Additive.resolve(&local, &remote, 1, 1);
707        match result {
708            OperationValue::NumberRef(s) => assert_eq!(s, "30"),
709            _ => panic!("Expected NumberRef"),
710        }
711    }
712
713    // Additional tests for coverage
714
715    #[test]
716    fn test_vector_clock_new() {
717        let vc = VectorClock::new();
718        assert_eq!(vc.get("any_replica"), 0);
719    }
720
721    #[test]
722    fn test_vector_clock_default() {
723        let vc = VectorClock::default();
724        assert_eq!(vc.get("any"), 0);
725    }
726
727    #[test]
728    fn test_vector_clock_increment_multiple_replicas() {
729        let mut vc = VectorClock::new();
730        vc.increment("a");
731        vc.increment("b");
732        vc.increment("c");
733        vc.increment("a");
734
735        assert_eq!(vc.get("a"), 2);
736        assert_eq!(vc.get("b"), 1);
737        assert_eq!(vc.get("c"), 1);
738        assert_eq!(vc.get("d"), 0);
739    }
740
741    #[test]
742    fn test_vector_clock_merge() {
743        let mut vc1 = VectorClock::new();
744        vc1.increment("a");
745        vc1.increment("a");
746
747        let mut vc2 = VectorClock::new();
748        vc2.increment("b");
749        vc2.increment("b");
750        vc2.increment("b");
751
752        vc1.merge(&vc2);
753
754        assert_eq!(vc1.get("a"), 2);
755        assert_eq!(vc1.get("b"), 3);
756    }
757
758    #[test]
759    fn test_vector_clock_merge_overlapping() {
760        let mut vc1 = VectorClock::new();
761        vc1.increment("a");
762
763        let mut vc2 = VectorClock::new();
764        vc2.increment("a");
765        vc2.increment("a");
766        vc2.increment("a");
767
768        vc1.merge(&vc2);
769        // Should take max
770        assert_eq!(vc1.get("a"), 3);
771    }
772
773    #[test]
774    fn test_vector_clock_clocks() {
775        let mut vc = VectorClock::new();
776        vc.increment("replica_a");
777        vc.increment("replica_b");
778
779        let clocks = vc.clocks();
780        assert!(clocks.contains_key("replica_a"));
781        assert!(clocks.contains_key("replica_b"));
782    }
783
784    #[test]
785    fn test_vector_clock_happened_before_empty() {
786        let vc1 = VectorClock::new();
787        let mut vc2 = VectorClock::new();
788        vc2.increment("a");
789
790        // Empty clock happened before non-empty
791        assert!(vc1.happened_before(&vc2));
792    }
793
794    #[test]
795    fn test_vector_clock_happened_before_equal() {
796        let mut vc1 = VectorClock::new();
797        vc1.increment("a");
798
799        let vc2 = vc1.clone();
800
801        // Equal clocks - neither happened before
802        assert!(!vc1.happened_before(&vc2));
803        assert!(!vc2.happened_before(&vc1));
804    }
805
806    #[test]
807    fn test_vector_clock_concurrent_with_both_have_unique() {
808        let mut vc1 = VectorClock::new();
809        vc1.increment("a");
810
811        let mut vc2 = VectorClock::new();
812        vc2.increment("b");
813
814        // Each has changes the other doesn't
815        assert!(vc1.concurrent_with(&vc2));
816        assert!(vc2.concurrent_with(&vc1));
817    }
818
819    #[test]
820    fn test_vector_clock_clone() {
821        let mut vc = VectorClock::new();
822        vc.increment("a");
823        vc.increment("b");
824
825        let cloned = vc.clone();
826        assert_eq!(cloned.get("a"), 1);
827        assert_eq!(cloned.get("b"), 1);
828    }
829
830    #[test]
831    fn test_vector_clock_eq() {
832        let mut vc1 = VectorClock::new();
833        vc1.increment("a");
834
835        let mut vc2 = VectorClock::new();
836        vc2.increment("a");
837
838        // Note: PartialEq compares the inline vectors, might not be equal due to hash order
839        let _ = vc1 == vc2;
840    }
841
842    #[test]
843    fn test_vector_clock_debug() {
844        let vc = VectorClock::new();
845        let debug = format!("{vc:?}");
846        assert!(debug.contains("VectorClock"));
847    }
848
849    #[test]
850    fn test_crdt_operation_fields() {
851        let op = CrdtOperation {
852            operation: DsonOperation::FieldAdd {
853                path: "test".to_string(),
854                value: OperationValue::StringRef("value".to_string()),
855            },
856            timestamp: 100,
857            replica_id: "replica_1".to_string(),
858            vector_clock: VectorClock::new(),
859        };
860
861        assert_eq!(op.timestamp, 100);
862        assert_eq!(op.replica_id, "replica_1");
863    }
864
865    #[test]
866    fn test_crdt_operation_clone() {
867        let op = CrdtOperation {
868            operation: DsonOperation::FieldAdd {
869                path: "test".to_string(),
870                value: OperationValue::Null,
871            },
872            timestamp: 50,
873            replica_id: "r1".to_string(),
874            vector_clock: VectorClock::new(),
875        };
876
877        let cloned = op;
878        assert_eq!(cloned.timestamp, 50);
879    }
880
881    #[test]
882    fn test_crdt_operation_debug() {
883        let op = CrdtOperation {
884            operation: DsonOperation::FieldDelete {
885                path: "test".to_string(),
886            },
887            timestamp: 0,
888            replica_id: "r".to_string(),
889            vector_clock: VectorClock::new(),
890        };
891
892        let debug = format!("{op:?}");
893        assert!(debug.contains("CrdtOperation"));
894    }
895
896    #[test]
897    fn test_merge_conflict_fields() {
898        let conflict = MergeConflict {
899            path: "user.name".to_string(),
900            local_value: OperationValue::StringRef("local".to_string()),
901            remote_value: OperationValue::StringRef("remote".to_string()),
902            local_timestamp: 100,
903            remote_timestamp: 200,
904            resolved_value: None,
905        };
906
907        assert_eq!(conflict.path, "user.name");
908        assert_eq!(conflict.local_timestamp, 100);
909        assert_eq!(conflict.remote_timestamp, 200);
910        assert!(conflict.resolved_value.is_none());
911    }
912
913    #[test]
914    fn test_merge_conflict_with_resolved() {
915        let conflict = MergeConflict {
916            path: "counter".to_string(),
917            local_value: OperationValue::NumberRef("10".to_string()),
918            remote_value: OperationValue::NumberRef("20".to_string()),
919            local_timestamp: 1,
920            remote_timestamp: 2,
921            resolved_value: Some(OperationValue::NumberRef("20".to_string())),
922        };
923
924        assert!(conflict.resolved_value.is_some());
925    }
926
927    #[test]
928    fn test_merge_conflict_clone() {
929        let conflict = MergeConflict {
930            path: "test".to_string(),
931            local_value: OperationValue::Null,
932            remote_value: OperationValue::Null,
933            local_timestamp: 0,
934            remote_timestamp: 0,
935            resolved_value: None,
936        };
937
938        let cloned = conflict;
939        assert_eq!(cloned.path, "test");
940    }
941
942    #[test]
943    fn test_merge_conflict_debug() {
944        let conflict = MergeConflict {
945            path: "p".to_string(),
946            local_value: OperationValue::Null,
947            remote_value: OperationValue::Null,
948            local_timestamp: 0,
949            remote_timestamp: 0,
950            resolved_value: None,
951        };
952
953        let debug = format!("{conflict:?}");
954        assert!(debug.contains("MergeConflict"));
955    }
956
957    #[test]
958    fn test_merge_strategy_min() {
959        let local = OperationValue::NumberRef("10".to_string());
960        let remote = OperationValue::NumberRef("5".to_string());
961
962        let result = MergeStrategy::Min.resolve(&local, &remote, 1, 1);
963        assert_eq!(result, remote); // 5 is smaller than 10
964    }
965
966    #[test]
967    fn test_merge_strategy_min_local_smaller() {
968        let local = OperationValue::NumberRef("3".to_string());
969        let remote = OperationValue::NumberRef("10".to_string());
970
971        let result = MergeStrategy::Min.resolve(&local, &remote, 1, 1);
972        assert_eq!(result, local); // 3 is smaller than 10
973    }
974
975    #[test]
976    fn test_merge_strategy_min_non_numeric() {
977        let local = OperationValue::StringRef("local".to_string());
978        let remote = OperationValue::StringRef("remote".to_string());
979
980        // Falls back to LWW for non-numeric
981        let result = MergeStrategy::Min.resolve(&local, &remote, 1, 2);
982        assert_eq!(result, remote);
983    }
984
985    #[test]
986    fn test_merge_strategy_max_local_larger() {
987        let local = OperationValue::NumberRef("100".to_string());
988        let remote = OperationValue::NumberRef("50".to_string());
989
990        let result = MergeStrategy::Max.resolve(&local, &remote, 1, 1);
991        assert_eq!(result, local); // 100 is larger
992    }
993
994    #[test]
995    fn test_merge_strategy_max_non_numeric() {
996        let local = OperationValue::StringRef("a".to_string());
997        let remote = OperationValue::StringRef("b".to_string());
998
999        // Falls back to LWW for non-numeric
1000        let result = MergeStrategy::Max.resolve(&local, &remote, 1, 2);
1001        assert_eq!(result, remote);
1002    }
1003
1004    #[test]
1005    fn test_merge_strategy_additive_strings() {
1006        let local = OperationValue::StringRef("hello ".to_string());
1007        let remote = OperationValue::StringRef("world".to_string());
1008
1009        let result = MergeStrategy::Additive.resolve(&local, &remote, 1, 1);
1010        match result {
1011            OperationValue::StringRef(s) => assert_eq!(s, "hello world"),
1012            _ => panic!("Expected StringRef"),
1013        }
1014    }
1015
1016    #[test]
1017    fn test_merge_strategy_additive_non_matching() {
1018        let local = OperationValue::BoolRef(true);
1019        let remote = OperationValue::NumberRef("10".to_string());
1020
1021        // Falls back to LWW for non-matching types
1022        let result = MergeStrategy::Additive.resolve(&local, &remote, 1, 2);
1023        assert_eq!(result, remote);
1024    }
1025
1026    #[test]
1027    fn test_merge_strategy_union() {
1028        let local = OperationValue::StringRef("a".to_string());
1029        let remote = OperationValue::StringRef("b".to_string());
1030
1031        // Union falls back to LWW for scalars
1032        let result = MergeStrategy::Union.resolve(&local, &remote, 1, 2);
1033        assert_eq!(result, remote);
1034    }
1035
1036    #[test]
1037    fn test_merge_strategy_custom() {
1038        let local = OperationValue::StringRef("local".to_string());
1039        let remote = OperationValue::StringRef("remote".to_string());
1040
1041        // Custom falls back to LWW
1042        let result = MergeStrategy::Custom("my_custom".to_string()).resolve(&local, &remote, 1, 2);
1043        assert_eq!(result, remote);
1044    }
1045
1046    #[test]
1047    fn test_implementation_characteristics_default() {
1048        let chars = ImplementationCharacteristics::default();
1049        assert!(!chars.zero_copy);
1050        assert!(!chars.simd_accelerated);
1051        assert!(!chars.streaming);
1052        assert!(!chars.crdt_support);
1053        assert!(!chars.schema_filtering);
1054        assert!(!chars.parallel);
1055        assert_eq!(chars.memory_overhead, 0);
1056        assert_eq!(chars.max_document_size, 0);
1057    }
1058
1059    #[test]
1060    fn test_implementation_characteristics_custom() {
1061        let chars = ImplementationCharacteristics {
1062            zero_copy: true,
1063            simd_accelerated: true,
1064            streaming: true,
1065            crdt_support: true,
1066            schema_filtering: true,
1067            parallel: true,
1068            memory_overhead: 1024,
1069            max_document_size: 1_000_000,
1070        };
1071
1072        assert!(chars.zero_copy);
1073        assert!(chars.simd_accelerated);
1074        assert_eq!(chars.memory_overhead, 1024);
1075    }
1076
1077    #[test]
1078    fn test_implementation_characteristics_clone() {
1079        let chars = ImplementationCharacteristics {
1080            zero_copy: true,
1081            ..Default::default()
1082        };
1083
1084        let cloned = chars;
1085        assert!(cloned.zero_copy);
1086    }
1087
1088    #[test]
1089    fn test_implementation_characteristics_debug() {
1090        let chars = ImplementationCharacteristics::default();
1091        let debug = format!("{chars:?}");
1092        assert!(debug.contains("ImplementationCharacteristics"));
1093    }
1094
1095    #[test]
1096    fn test_vector_clock_hash_replica_id_consistency() {
1097        // Test that hashing is consistent
1098        let hash1 = VectorClock::hash_replica_id("replica_a");
1099        let hash2 = VectorClock::hash_replica_id("replica_a");
1100        assert_eq!(hash1, hash2);
1101
1102        // Different IDs should have different hashes (with high probability)
1103        let hash3 = VectorClock::hash_replica_id("replica_b");
1104        assert_ne!(hash1, hash3);
1105    }
1106
1107    #[test]
1108    fn test_vector_clock_get_by_hash() {
1109        let mut vc = VectorClock::new();
1110        vc.increment("test_replica");
1111
1112        let hash = VectorClock::hash_replica_id("test_replica");
1113        assert_eq!(vc.get_by_hash(hash), 1);
1114
1115        // Non-existent hash
1116        let fake_hash = 12_345_678;
1117        assert_eq!(vc.get_by_hash(fake_hash), 0);
1118    }
1119
1120    #[test]
1121    fn test_merge_strategy_max_invalid_parse() {
1122        // Test with non-parseable numbers
1123        let local = OperationValue::NumberRef("invalid".to_string());
1124        let remote = OperationValue::NumberRef("5".to_string());
1125
1126        let result = MergeStrategy::Max.resolve(&local, &remote, 1, 1);
1127        // "invalid" parses to 0.0, so 5 wins
1128        assert_eq!(result, remote);
1129    }
1130
1131    #[test]
1132    fn test_merge_strategy_min_invalid_parse() {
1133        let local = OperationValue::NumberRef("invalid".to_string());
1134        let remote = OperationValue::NumberRef("5".to_string());
1135
1136        let result = MergeStrategy::Min.resolve(&local, &remote, 1, 1);
1137        // "invalid" parses to 0.0, so 0 wins (local)
1138        assert_eq!(result, local);
1139    }
1140
1141    #[test]
1142    fn test_merge_strategy_additive_invalid_parse() {
1143        let local = OperationValue::NumberRef("invalid".to_string());
1144        let remote = OperationValue::NumberRef("10".to_string());
1145
1146        let result = MergeStrategy::Additive.resolve(&local, &remote, 1, 1);
1147        // "invalid" parses to 0.0, so result is 0 + 10 = 10
1148        match result {
1149            OperationValue::NumberRef(s) => assert_eq!(s, "10"),
1150            _ => panic!("Expected NumberRef"),
1151        }
1152    }
1153
1154    #[test]
1155    fn test_vector_clock_merge_with_more_replicas() {
1156        let mut vc1 = VectorClock::new();
1157        vc1.increment("a");
1158
1159        let mut vc2 = VectorClock::new();
1160        vc2.increment("b");
1161        vc2.increment("c");
1162        vc2.increment("d");
1163
1164        vc1.merge(&vc2);
1165
1166        assert_eq!(vc1.get("a"), 1);
1167        assert_eq!(vc1.get("b"), 1);
1168        assert_eq!(vc1.get("c"), 1);
1169        assert_eq!(vc1.get("d"), 1);
1170    }
1171}