Skip to main content

grafeo_common/types/
value.rs

1//! Property values and keys for nodes and edges.
2//!
3//! [`Value`] is the dynamic type that can hold any property value - strings,
4//! numbers, lists, maps, etc. [`PropertyKey`] is an interned string for
5//! efficient property lookups.
6
7use arcstr::ArcStr;
8use serde::{Deserialize, Serialize};
9use std::collections::BTreeMap;
10use std::fmt;
11use std::hash::{Hash, Hasher};
12use std::sync::Arc;
13
14use super::{Date, Duration, Time, Timestamp, ZonedDatetime};
15
16/// An interned property name - cheap to clone and compare.
17///
18/// Property names like "name", "age", "created_at" get used repeatedly, so
19/// we intern them with `ArcStr`. You can create these from strings directly.
20#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
21pub struct PropertyKey(ArcStr);
22
23impl PropertyKey {
24    /// Creates a new property key from a string.
25    #[must_use]
26    pub fn new(s: impl Into<ArcStr>) -> Self {
27        Self(s.into())
28    }
29
30    /// Returns the string representation.
31    #[must_use]
32    pub fn as_str(&self) -> &str {
33        &self.0
34    }
35}
36
37impl fmt::Debug for PropertyKey {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        write!(f, "PropertyKey({:?})", self.0)
40    }
41}
42
43impl fmt::Display for PropertyKey {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        write!(f, "{}", self.0)
46    }
47}
48
49impl From<&str> for PropertyKey {
50    fn from(s: &str) -> Self {
51        Self::new(s)
52    }
53}
54
55impl From<String> for PropertyKey {
56    fn from(s: String) -> Self {
57        Self::new(s)
58    }
59}
60
61impl AsRef<str> for PropertyKey {
62    fn as_ref(&self) -> &str {
63        &self.0
64    }
65}
66
67impl std::borrow::Borrow<str> for PropertyKey {
68    fn borrow(&self) -> &str {
69        &self.0
70    }
71}
72
73/// A dynamically-typed property value.
74///
75/// Nodes and edges can have properties of various types - this enum holds
76/// them all. Follows the GQL type system, so you can store nulls, booleans,
77/// numbers, strings, timestamps, lists, and maps.
78///
79/// # Examples
80///
81/// ```
82/// use grafeo_common::types::Value;
83///
84/// let name = Value::from("Alix");
85/// let age = Value::from(30i64);
86/// let active = Value::from(true);
87///
88/// // Check types
89/// assert!(name.as_str().is_some());
90/// assert_eq!(age.as_int64(), Some(30));
91/// ```
92#[derive(Clone, PartialEq, Serialize, Deserialize)]
93#[non_exhaustive]
94pub enum Value {
95    /// Null/missing value
96    Null,
97
98    /// Boolean value
99    Bool(bool),
100
101    /// 64-bit signed integer
102    Int64(i64),
103
104    /// 64-bit floating point
105    Float64(f64),
106
107    /// UTF-8 string (uses ArcStr for cheap cloning)
108    String(ArcStr),
109
110    /// Binary data
111    Bytes(Arc<[u8]>),
112
113    /// Timestamp with timezone
114    Timestamp(Timestamp),
115
116    /// Calendar date (days since 1970-01-01)
117    Date(Date),
118
119    /// Time of day with optional UTC offset
120    Time(Time),
121
122    /// ISO 8601 duration (months, days, nanos)
123    Duration(Duration),
124
125    /// Datetime with a fixed UTC offset
126    ZonedDatetime(ZonedDatetime),
127
128    /// Ordered list of values
129    List(Arc<[Value]>),
130
131    /// Key-value map (uses BTreeMap for deterministic ordering)
132    Map(Arc<BTreeMap<PropertyKey, Value>>),
133
134    /// Fixed-size vector of 32-bit floats for embeddings.
135    ///
136    /// Uses f32 for 4x compression vs f64. Arc for cheap cloning.
137    /// Dimension is implicit from length. Common dimensions: 384, 768, 1536.
138    Vector(Arc<[f32]>),
139
140    /// Graph path: alternating sequence of nodes and edges.
141    ///
142    /// Nodes and edges are stored as lists of values (typically node/edge maps
143    /// with `_id`, `_labels`/`_type`, and properties). The invariant is that
144    /// `edges.len() == nodes.len() - 1` for a valid path.
145    Path {
146        /// Nodes along the path, from source to target.
147        nodes: Arc<[Value]>,
148        /// Edges along the path, connecting consecutive nodes.
149        edges: Arc<[Value]>,
150    },
151
152    /// Grow-only counter (GCounter) for conflict-free distributed counting.
153    ///
154    /// Stores per-replica contribution counts. The logical value is the sum
155    /// of all entries. Merge is per-replica max, making it commutative and
156    /// idempotent.
157    ///
158    /// Key: replica ID string. Value: that replica's running total (grows
159    /// monotonically).
160    GCounter(Arc<std::collections::HashMap<String, u64>>),
161
162    /// Positive-negative counter (ON-Counter) for distributed increment and
163    /// decrement.
164    ///
165    /// Two grow-only maps track positive and negative contributions
166    /// independently. The logical value is `sum(pos) − sum(neg)`. Merge is
167    /// per-replica max on each map independently.
168    OnCounter {
169        /// Per-replica positive contributions (increments).
170        pos: Arc<std::collections::HashMap<String, u64>>,
171        /// Per-replica negative contributions (decrements, stored as positive
172        /// magnitudes).
173        neg: Arc<std::collections::HashMap<String, u64>>,
174    },
175}
176
177impl Value {
178    /// Returns `true` if this value is null.
179    #[inline]
180    #[must_use]
181    pub const fn is_null(&self) -> bool {
182        matches!(self, Value::Null)
183    }
184
185    /// Returns the boolean value if this is a Bool, otherwise None.
186    #[inline]
187    #[must_use]
188    pub const fn as_bool(&self) -> Option<bool> {
189        match self {
190            Value::Bool(b) => Some(*b),
191            _ => None,
192        }
193    }
194
195    /// Returns the integer value if this is an Int64, otherwise None.
196    #[inline]
197    #[must_use]
198    pub const fn as_int64(&self) -> Option<i64> {
199        match self {
200            Value::Int64(i) => Some(*i),
201            _ => None,
202        }
203    }
204
205    /// Returns the float value if this is a Float64, otherwise None.
206    #[inline]
207    #[must_use]
208    pub const fn as_float64(&self) -> Option<f64> {
209        match self {
210            Value::Float64(f) => Some(*f),
211            _ => None,
212        }
213    }
214
215    /// Returns the string value if this is a String, otherwise None.
216    #[inline]
217    #[must_use]
218    pub fn as_str(&self) -> Option<&str> {
219        match self {
220            Value::String(s) => Some(s),
221            _ => None,
222        }
223    }
224
225    /// Returns the bytes value if this is Bytes, otherwise None.
226    #[inline]
227    #[must_use]
228    pub fn as_bytes(&self) -> Option<&[u8]> {
229        match self {
230            Value::Bytes(b) => Some(b),
231            _ => None,
232        }
233    }
234
235    /// Returns the timestamp value if this is a Timestamp, otherwise None.
236    #[inline]
237    #[must_use]
238    pub const fn as_timestamp(&self) -> Option<Timestamp> {
239        match self {
240            Value::Timestamp(t) => Some(*t),
241            _ => None,
242        }
243    }
244
245    /// Returns the date value if this is a Date, otherwise None.
246    #[inline]
247    #[must_use]
248    pub const fn as_date(&self) -> Option<Date> {
249        match self {
250            Value::Date(d) => Some(*d),
251            _ => None,
252        }
253    }
254
255    /// Returns the time value if this is a Time, otherwise None.
256    #[inline]
257    #[must_use]
258    pub const fn as_time(&self) -> Option<Time> {
259        match self {
260            Value::Time(t) => Some(*t),
261            _ => None,
262        }
263    }
264
265    /// Returns the duration value if this is a Duration, otherwise None.
266    #[inline]
267    #[must_use]
268    pub const fn as_duration(&self) -> Option<Duration> {
269        match self {
270            Value::Duration(d) => Some(*d),
271            _ => None,
272        }
273    }
274
275    /// Returns the zoned datetime value if this is a ZonedDatetime, otherwise None.
276    #[inline]
277    #[must_use]
278    pub const fn as_zoned_datetime(&self) -> Option<ZonedDatetime> {
279        match self {
280            Value::ZonedDatetime(zdt) => Some(*zdt),
281            _ => None,
282        }
283    }
284
285    /// Returns the list value if this is a List, otherwise None.
286    #[inline]
287    #[must_use]
288    pub fn as_list(&self) -> Option<&[Value]> {
289        match self {
290            Value::List(l) => Some(l),
291            _ => None,
292        }
293    }
294
295    /// Returns the map value if this is a Map, otherwise None.
296    #[inline]
297    #[must_use]
298    pub fn as_map(&self) -> Option<&BTreeMap<PropertyKey, Value>> {
299        match self {
300            Value::Map(m) => Some(m),
301            _ => None,
302        }
303    }
304
305    /// Returns the vector if this is a Vector, otherwise None.
306    #[inline]
307    #[must_use]
308    pub fn as_vector(&self) -> Option<&[f32]> {
309        match self {
310            Value::Vector(v) => Some(v),
311            _ => None,
312        }
313    }
314
315    /// Returns the path components if this is a Path, otherwise None.
316    #[inline]
317    #[must_use]
318    pub fn as_path(&self) -> Option<(&[Value], &[Value])> {
319        match self {
320            Value::Path { nodes, edges } => Some((nodes, edges)),
321            _ => None,
322        }
323    }
324
325    /// Returns true if this is a vector type.
326    #[inline]
327    #[must_use]
328    pub const fn is_vector(&self) -> bool {
329        matches!(self, Value::Vector(_))
330    }
331
332    /// Returns the vector dimensions if this is a Vector.
333    #[inline]
334    #[must_use]
335    pub fn vector_dimensions(&self) -> Option<usize> {
336        match self {
337            Value::Vector(v) => Some(v.len()),
338            _ => None,
339        }
340    }
341
342    /// Returns the type name of this value.
343    #[must_use]
344    pub const fn type_name(&self) -> &'static str {
345        match self {
346            Value::Null => "NULL",
347            Value::Bool(_) => "BOOL",
348            Value::Int64(_) => "INT64",
349            Value::Float64(_) => "FLOAT64",
350            Value::String(_) => "STRING",
351            Value::Bytes(_) => "BYTES",
352            Value::Timestamp(_) => "TIMESTAMP",
353            Value::Date(_) => "DATE",
354            Value::Time(_) => "TIME",
355            Value::Duration(_) => "DURATION",
356            Value::ZonedDatetime(_) => "ZONED DATETIME",
357            Value::List(_) => "LIST",
358            Value::Map(_) => "MAP",
359            Value::Vector(_) => "VECTOR",
360            Value::Path { .. } => "PATH",
361            Value::GCounter(_) => "GCOUNTER",
362            Value::OnCounter { .. } => "PNCOUNTER",
363        }
364    }
365
366    /// Serializes this value to bytes.
367    ///
368    /// # Errors
369    ///
370    /// Returns an error if the value cannot be encoded (e.g. deeply nested structures).
371    pub fn serialize(&self) -> Result<Vec<u8>, bincode::error::EncodeError> {
372        bincode::serde::encode_to_vec(self, bincode::config::standard())
373    }
374
375    /// Deserializes a value from bytes.
376    ///
377    /// # Errors
378    ///
379    /// Returns an error if the bytes do not represent a valid Value.
380    pub fn deserialize(bytes: &[u8]) -> Result<Self, bincode::error::DecodeError> {
381        let (value, _) = bincode::serde::decode_from_slice(bytes, bincode::config::standard())?;
382        Ok(value)
383    }
384
385    /// Returns an estimate of the heap memory used by this value in bytes.
386    ///
387    /// This is a best-effort approximation used for enforcing property size
388    /// limits. Fixed-size types return 0 (only the enum discriminant, which
389    /// is already accounted for on the stack).
390    #[must_use]
391    pub fn estimated_size_bytes(&self) -> usize {
392        match self {
393            Value::Null
394            | Value::Bool(_)
395            | Value::Int64(_)
396            | Value::Float64(_)
397            | Value::Timestamp(_)
398            | Value::Date(_)
399            | Value::Time(_)
400            | Value::Duration(_)
401            | Value::ZonedDatetime(_) => 0,
402            Value::String(s) => s.len(),
403            Value::Bytes(b) => b.len(),
404            Value::Vector(v) => v.len() * size_of::<f32>(),
405            Value::List(items) => {
406                items.iter().map(Value::estimated_size_bytes).sum::<usize>()
407                    + items.len() * size_of::<Value>()
408            }
409            Value::Map(m) => m
410                .iter()
411                .map(|(k, v)| k.as_ref().len() + v.estimated_size_bytes() + size_of::<Value>())
412                .sum(),
413            Value::Path { nodes, edges } => {
414                let n: usize = nodes.iter().map(Value::estimated_size_bytes).sum::<usize>()
415                    + nodes.len() * size_of::<Value>();
416                let e: usize = edges.iter().map(Value::estimated_size_bytes).sum::<usize>()
417                    + edges.len() * size_of::<Value>();
418                n + e
419            }
420            Value::GCounter(m) => m.keys().map(|k| k.len() + size_of::<u64>()).sum(),
421            Value::OnCounter { pos, neg } => {
422                let p: usize = pos.keys().map(|k| k.len() + size_of::<u64>()).sum();
423                let n: usize = neg.keys().map(|k| k.len() + size_of::<u64>()).sum();
424                p + n
425            }
426        }
427    }
428}
429
430impl fmt::Debug for Value {
431    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432        match self {
433            Value::Null => write!(f, "Null"),
434            Value::Bool(b) => write!(f, "Bool({b})"),
435            Value::Int64(i) => write!(f, "Int64({i})"),
436            Value::Float64(fl) => write!(f, "Float64({fl})"),
437            Value::String(s) => write!(f, "String({s:?})"),
438            Value::Bytes(b) => write!(f, "Bytes([{}; {} bytes])", b.first().unwrap_or(&0), b.len()),
439            Value::Timestamp(t) => write!(f, "Timestamp({t:?})"),
440            Value::Date(d) => write!(f, "Date({d})"),
441            Value::Time(t) => write!(f, "Time({t})"),
442            Value::Duration(d) => write!(f, "Duration({d})"),
443            Value::ZonedDatetime(zdt) => write!(f, "ZonedDatetime({zdt})"),
444            Value::List(l) => write!(f, "List({l:?})"),
445            Value::Map(m) => write!(f, "Map({m:?})"),
446            Value::Vector(v) => write!(
447                f,
448                "Vector([{}; {} dims])",
449                v.first().unwrap_or(&0.0),
450                v.len()
451            ),
452            Value::Path { nodes, edges } => {
453                write!(f, "Path({} nodes, {} edges)", nodes.len(), edges.len())
454            }
455            Value::GCounter(counts) => {
456                let total: u64 = counts.values().sum();
457                write!(f, "GCounter(total={total}, replicas={})", counts.len())
458            }
459            Value::OnCounter { pos, neg } => {
460                let pos_sum: u128 = pos.values().copied().map(u128::from).sum();
461                let neg_sum: u128 = neg.values().copied().map(u128::from).sum();
462                if pos_sum >= neg_sum {
463                    write!(f, "OnCounter(net={})", pos_sum - neg_sum)
464                } else {
465                    write!(f, "OnCounter(net=-{})", neg_sum - pos_sum)
466                }
467            }
468        }
469    }
470}
471
472impl fmt::Display for Value {
473    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474        match self {
475            Value::Null => write!(f, "NULL"),
476            Value::Bool(b) => write!(f, "{b}"),
477            Value::Int64(i) => write!(f, "{i}"),
478            Value::Float64(fl) => write!(f, "{fl}"),
479            Value::String(s) => write!(f, "{s:?}"),
480            Value::Bytes(b) => write!(f, "<bytes: {} bytes>", b.len()),
481            Value::Timestamp(t) => write!(f, "{t}"),
482            Value::Date(d) => write!(f, "{d}"),
483            Value::Time(t) => write!(f, "{t}"),
484            Value::Duration(d) => write!(f, "{d}"),
485            Value::ZonedDatetime(zdt) => write!(f, "{zdt}"),
486            Value::List(l) => {
487                write!(f, "[")?;
488                for (i, v) in l.iter().enumerate() {
489                    if i > 0 {
490                        write!(f, ", ")?;
491                    }
492                    write!(f, "{v}")?;
493                }
494                write!(f, "]")
495            }
496            Value::Map(m) => {
497                write!(f, "{{")?;
498                for (i, (k, v)) in m.iter().enumerate() {
499                    if i > 0 {
500                        write!(f, ", ")?;
501                    }
502                    write!(f, "{k}: {v}")?;
503                }
504                write!(f, "}}")
505            }
506            Value::Vector(v) => {
507                write!(f, "vector([")?;
508                let show_count = v.len().min(3);
509                for (i, val) in v.iter().take(show_count).enumerate() {
510                    if i > 0 {
511                        write!(f, ", ")?;
512                    }
513                    write!(f, "{val}")?;
514                }
515                if v.len() > 3 {
516                    write!(f, ", ... ({} dims)", v.len())?;
517                }
518                write!(f, "])")
519            }
520            Value::Path { nodes, edges } => {
521                // Display path as alternating node-edge-node sequence
522                write!(f, "<")?;
523                for (i, node) in nodes.iter().enumerate() {
524                    if i > 0
525                        && let Some(edge) = edges.get(i - 1)
526                    {
527                        write!(f, "-[{edge}]-")?;
528                    }
529                    write!(f, "({node})")?;
530                }
531                write!(f, ">")
532            }
533            Value::GCounter(counts) => {
534                let total: u64 = counts.values().sum();
535                write!(f, "GCounter({total})")
536            }
537            Value::OnCounter { pos, neg } => {
538                let pos_sum: u128 = pos.values().copied().map(u128::from).sum();
539                let neg_sum: u128 = neg.values().copied().map(u128::from).sum();
540                if pos_sum >= neg_sum {
541                    write!(f, "OnCounter({})", pos_sum - neg_sum)
542                } else {
543                    write!(f, "OnCounter(-{})", neg_sum - pos_sum)
544                }
545            }
546        }
547    }
548}
549
550// Convenient From implementations
551impl From<bool> for Value {
552    fn from(b: bool) -> Self {
553        Value::Bool(b)
554    }
555}
556
557impl From<i64> for Value {
558    fn from(i: i64) -> Self {
559        Value::Int64(i)
560    }
561}
562
563impl From<i32> for Value {
564    fn from(i: i32) -> Self {
565        Value::Int64(i64::from(i))
566    }
567}
568
569impl From<f64> for Value {
570    fn from(f: f64) -> Self {
571        Value::Float64(f)
572    }
573}
574
575impl From<f32> for Value {
576    fn from(f: f32) -> Self {
577        Value::Float64(f64::from(f))
578    }
579}
580
581impl From<&str> for Value {
582    fn from(s: &str) -> Self {
583        Value::String(s.into())
584    }
585}
586
587impl From<String> for Value {
588    fn from(s: String) -> Self {
589        Value::String(s.into())
590    }
591}
592
593impl From<ArcStr> for Value {
594    fn from(s: ArcStr) -> Self {
595        Value::String(s)
596    }
597}
598
599impl From<Vec<u8>> for Value {
600    fn from(b: Vec<u8>) -> Self {
601        Value::Bytes(b.into())
602    }
603}
604
605impl From<&[u8]> for Value {
606    fn from(b: &[u8]) -> Self {
607        Value::Bytes(b.into())
608    }
609}
610
611impl From<Timestamp> for Value {
612    fn from(t: Timestamp) -> Self {
613        Value::Timestamp(t)
614    }
615}
616
617impl From<Date> for Value {
618    fn from(d: Date) -> Self {
619        Value::Date(d)
620    }
621}
622
623impl From<Time> for Value {
624    fn from(t: Time) -> Self {
625        Value::Time(t)
626    }
627}
628
629impl From<Duration> for Value {
630    fn from(d: Duration) -> Self {
631        Value::Duration(d)
632    }
633}
634
635impl From<ZonedDatetime> for Value {
636    fn from(zdt: ZonedDatetime) -> Self {
637        Value::ZonedDatetime(zdt)
638    }
639}
640
641impl<T: Into<Value>> From<Vec<T>> for Value {
642    fn from(v: Vec<T>) -> Self {
643        Value::List(v.into_iter().map(Into::into).collect())
644    }
645}
646
647impl From<&[f32]> for Value {
648    fn from(v: &[f32]) -> Self {
649        Value::Vector(v.into())
650    }
651}
652
653impl From<Arc<[f32]>> for Value {
654    fn from(v: Arc<[f32]>) -> Self {
655        Value::Vector(v)
656    }
657}
658
659impl<T: Into<Value>> From<Option<T>> for Value {
660    fn from(opt: Option<T>) -> Self {
661        match opt {
662            Some(v) => v.into(),
663            None => Value::Null,
664        }
665    }
666}
667
668/// A hashable wrapper around [`Value`] for use in hash-based indexes.
669///
670/// `Value` itself cannot implement `Hash` because it contains `f64` (which has
671/// NaN issues). This wrapper converts floats to their bit representation for
672/// hashing, allowing values to be used as keys in hash maps and sets.
673///
674/// # Note on Float Equality
675///
676/// Two `HashableValue`s containing `f64` are considered equal if they have
677/// identical bit representations. This means `NaN == NaN` (same bits) and
678/// positive/negative zero are considered different.
679#[derive(Clone, Debug)]
680pub struct HashableValue(pub Value);
681
682/// An orderable wrapper around [`Value`] for use in B-tree indexes and range queries.
683///
684/// `Value` itself cannot implement `Ord` because `f64` doesn't implement `Ord`
685/// (due to NaN). This wrapper provides total ordering for comparable value types,
686/// enabling use in `BTreeMap`, `BTreeSet`, and range queries.
687///
688/// # Supported Types
689///
690/// - `Int64` - standard integer ordering
691/// - `Float64` - total ordering (NaN treated as greater than all other values)
692/// - `String` - lexicographic ordering
693/// - `Bool` - false < true
694/// - `Timestamp` - chronological ordering
695///
696/// Other types (`Null`, `Bytes`, `List`, `Map`) return `Err(())` from `try_from`.
697///
698/// # Examples
699///
700/// ```
701/// use grafeo_common::types::{OrderableValue, Value};
702/// use std::collections::BTreeSet;
703///
704/// let mut set = BTreeSet::new();
705/// set.insert(OrderableValue::try_from(&Value::Int64(30)).unwrap());
706/// set.insert(OrderableValue::try_from(&Value::Int64(10)).unwrap());
707/// set.insert(OrderableValue::try_from(&Value::Int64(20)).unwrap());
708///
709/// // Iterates in sorted order: 10, 20, 30
710/// let values: Vec<_> = set.iter().map(|v| v.as_i64().unwrap()).collect();
711/// assert_eq!(values, vec![10, 20, 30]);
712/// ```
713#[derive(Clone, Debug)]
714#[non_exhaustive]
715pub enum OrderableValue {
716    /// 64-bit signed integer
717    Int64(i64),
718    /// 64-bit floating point with total ordering (NaN > everything)
719    Float64(OrderedFloat64),
720    /// UTF-8 string
721    String(ArcStr),
722    /// Boolean value (false < true)
723    Bool(bool),
724    /// Timestamp (microseconds since epoch)
725    Timestamp(Timestamp),
726    /// Calendar date (days since epoch)
727    Date(Date),
728    /// Time of day with optional offset
729    Time(Time),
730    /// Datetime with a fixed UTC offset
731    ZonedDatetime(ZonedDatetime),
732}
733
734/// A wrapper around `f64` that implements `Ord` with total ordering.
735///
736/// NaN values are treated as greater than all other values (including infinity).
737/// Negative zero is considered equal to positive zero.
738#[derive(Clone, Copy, Debug)]
739pub struct OrderedFloat64(pub f64);
740
741impl OrderedFloat64 {
742    /// Creates a new ordered float.
743    #[must_use]
744    pub const fn new(f: f64) -> Self {
745        Self(f)
746    }
747
748    /// Returns the inner f64 value.
749    #[must_use]
750    pub const fn get(&self) -> f64 {
751        self.0
752    }
753}
754
755impl PartialEq for OrderedFloat64 {
756    fn eq(&self, other: &Self) -> bool {
757        // Handle NaN: NaN equals NaN for consistency with Ord
758        match (self.0.is_nan(), other.0.is_nan()) {
759            (true, true) => true,
760            (true, false) | (false, true) => false,
761            (false, false) => self.0 == other.0,
762        }
763    }
764}
765
766impl Eq for OrderedFloat64 {}
767
768impl PartialOrd for OrderedFloat64 {
769    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
770        Some(self.cmp(other))
771    }
772}
773
774impl Ord for OrderedFloat64 {
775    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
776        // Handle NaN: NaN is greater than everything (including itself for consistency)
777        match (self.0.is_nan(), other.0.is_nan()) {
778            (true, true) => std::cmp::Ordering::Equal,
779            (true, false) => std::cmp::Ordering::Greater,
780            (false, true) => std::cmp::Ordering::Less,
781            (false, false) => {
782                // Normal comparison for non-NaN values
783                self.0
784                    .partial_cmp(&other.0)
785                    .unwrap_or(std::cmp::Ordering::Equal)
786            }
787        }
788    }
789}
790
791impl Hash for OrderedFloat64 {
792    fn hash<H: Hasher>(&self, state: &mut H) {
793        self.0.to_bits().hash(state);
794    }
795}
796
797impl From<f64> for OrderedFloat64 {
798    fn from(f: f64) -> Self {
799        Self(f)
800    }
801}
802
803impl TryFrom<&Value> for OrderableValue {
804    type Error = ();
805
806    /// Attempts to create an `OrderableValue` from a `Value`.
807    ///
808    /// Returns `Err(())` for types that don't have a natural ordering
809    /// (`Null`, `Bytes`, `List`, `Map`, `Vector`).
810    fn try_from(value: &Value) -> Result<Self, Self::Error> {
811        match value {
812            Value::Int64(i) => Ok(Self::Int64(*i)),
813            Value::Float64(f) => Ok(Self::Float64(OrderedFloat64(*f))),
814            Value::String(s) => Ok(Self::String(s.clone())),
815            Value::Bool(b) => Ok(Self::Bool(*b)),
816            Value::Timestamp(t) => Ok(Self::Timestamp(*t)),
817            Value::Date(d) => Ok(Self::Date(*d)),
818            Value::Time(t) => Ok(Self::Time(*t)),
819            Value::ZonedDatetime(zdt) => Ok(Self::ZonedDatetime(*zdt)),
820            Value::Null
821            | Value::Bytes(_)
822            | Value::Duration(_)
823            | Value::List(_)
824            | Value::Map(_)
825            | Value::Vector(_)
826            | Value::Path { .. }
827            | Value::GCounter(_)
828            | Value::OnCounter { .. } => Err(()),
829        }
830    }
831}
832
833impl OrderableValue {
834    /// Converts this `OrderableValue` back to a `Value`.
835    #[must_use]
836    pub fn into_value(self) -> Value {
837        match self {
838            Self::Int64(i) => Value::Int64(i),
839            Self::Float64(f) => Value::Float64(f.0),
840            Self::String(s) => Value::String(s),
841            Self::Bool(b) => Value::Bool(b),
842            Self::Timestamp(t) => Value::Timestamp(t),
843            Self::Date(d) => Value::Date(d),
844            Self::Time(t) => Value::Time(t),
845            Self::ZonedDatetime(zdt) => Value::ZonedDatetime(zdt),
846        }
847    }
848
849    /// Returns the value as an i64, if it's an Int64.
850    #[must_use]
851    pub const fn as_i64(&self) -> Option<i64> {
852        match self {
853            Self::Int64(i) => Some(*i),
854            _ => None,
855        }
856    }
857
858    /// Returns the value as an f64, if it's a Float64.
859    #[must_use]
860    pub const fn as_f64(&self) -> Option<f64> {
861        match self {
862            Self::Float64(f) => Some(f.0),
863            _ => None,
864        }
865    }
866
867    /// Returns the value as a string slice, if it's a String.
868    #[must_use]
869    pub fn as_str(&self) -> Option<&str> {
870        match self {
871            Self::String(s) => Some(s),
872            _ => None,
873        }
874    }
875}
876
877impl PartialEq for OrderableValue {
878    fn eq(&self, other: &Self) -> bool {
879        match (self, other) {
880            (Self::Int64(a), Self::Int64(b)) => a == b,
881            (Self::Float64(a), Self::Float64(b)) => a == b,
882            (Self::String(a), Self::String(b)) => a == b,
883            (Self::Bool(a), Self::Bool(b)) => a == b,
884            (Self::Timestamp(a), Self::Timestamp(b)) => a == b,
885            (Self::Date(a), Self::Date(b)) => a == b,
886            (Self::Time(a), Self::Time(b)) => a == b,
887            (Self::ZonedDatetime(a), Self::ZonedDatetime(b)) => a == b,
888            // Cross-type numeric comparison
889            (Self::Int64(a), Self::Float64(b)) => (*a as f64) == b.0,
890            (Self::Float64(a), Self::Int64(b)) => a.0 == (*b as f64),
891            _ => false,
892        }
893    }
894}
895
896impl Eq for OrderableValue {}
897
898impl PartialOrd for OrderableValue {
899    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
900        Some(self.cmp(other))
901    }
902}
903
904impl Ord for OrderableValue {
905    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
906        match (self, other) {
907            (Self::Int64(a), Self::Int64(b)) => a.cmp(b),
908            (Self::Float64(a), Self::Float64(b)) => a.cmp(b),
909            (Self::String(a), Self::String(b)) => a.cmp(b),
910            (Self::Bool(a), Self::Bool(b)) => a.cmp(b),
911            (Self::Timestamp(a), Self::Timestamp(b)) => a.cmp(b),
912            (Self::Date(a), Self::Date(b)) => a.cmp(b),
913            (Self::Time(a), Self::Time(b)) => a.cmp(b),
914            (Self::ZonedDatetime(a), Self::ZonedDatetime(b)) => a.cmp(b),
915            // Cross-type numeric comparison
916            (Self::Int64(a), Self::Float64(b)) => OrderedFloat64(*a as f64).cmp(b),
917            (Self::Float64(a), Self::Int64(b)) => a.cmp(&OrderedFloat64(*b as f64)),
918            // Different types: order by type ordinal for consistency
919            // Order: Bool < Int64 < Float64 < String < Timestamp < Date < Time < ZonedDatetime
920            _ => self.type_ordinal().cmp(&other.type_ordinal()),
921        }
922    }
923}
924
925impl OrderableValue {
926    /// Returns a numeric ordinal for consistent cross-type ordering.
927    const fn type_ordinal(&self) -> u8 {
928        match self {
929            Self::Bool(_) => 0,
930            Self::Int64(_) => 1,
931            Self::Float64(_) => 2,
932            Self::String(_) => 3,
933            Self::Timestamp(_) => 4,
934            Self::Date(_) => 5,
935            Self::Time(_) => 6,
936            Self::ZonedDatetime(_) => 7,
937        }
938    }
939}
940
941impl Hash for OrderableValue {
942    fn hash<H: Hasher>(&self, state: &mut H) {
943        std::mem::discriminant(self).hash(state);
944        match self {
945            Self::Int64(i) => i.hash(state),
946            Self::Float64(f) => f.hash(state),
947            Self::String(s) => s.hash(state),
948            Self::Bool(b) => b.hash(state),
949            Self::Timestamp(t) => t.hash(state),
950            Self::Date(d) => d.hash(state),
951            Self::Time(t) => t.hash(state),
952            Self::ZonedDatetime(zdt) => zdt.hash(state),
953        }
954    }
955}
956
957impl HashableValue {
958    /// Creates a new hashable value from a value.
959    #[must_use]
960    pub fn new(value: Value) -> Self {
961        Self(value)
962    }
963
964    /// Returns a reference to the inner value.
965    #[must_use]
966    pub fn inner(&self) -> &Value {
967        &self.0
968    }
969
970    /// Consumes the wrapper and returns the inner value.
971    #[must_use]
972    pub fn into_inner(self) -> Value {
973        self.0
974    }
975}
976
977/// Hashes a `Value` by reference without cloning nested values.
978fn hash_value<H: Hasher>(value: &Value, state: &mut H) {
979    std::mem::discriminant(value).hash(state);
980
981    match value {
982        Value::Null => {}
983        Value::Bool(b) => b.hash(state),
984        Value::Int64(i) => i.hash(state),
985        Value::Float64(f) => f.to_bits().hash(state),
986        Value::String(s) => s.hash(state),
987        Value::Bytes(b) => b.hash(state),
988        Value::Timestamp(t) => t.hash(state),
989        Value::Date(d) => d.hash(state),
990        Value::Time(t) => t.hash(state),
991        Value::Duration(d) => d.hash(state),
992        Value::ZonedDatetime(zdt) => zdt.hash(state),
993        Value::List(l) => {
994            l.len().hash(state);
995            for v in l.iter() {
996                hash_value(v, state);
997            }
998        }
999        Value::Map(m) => {
1000            m.len().hash(state);
1001            for (k, v) in m.iter() {
1002                k.hash(state);
1003                hash_value(v, state);
1004            }
1005        }
1006        Value::Vector(v) => {
1007            v.len().hash(state);
1008            for &f in v.iter() {
1009                f.to_bits().hash(state);
1010            }
1011        }
1012        Value::Path { nodes, edges } => {
1013            nodes.len().hash(state);
1014            for v in nodes.iter() {
1015                hash_value(v, state);
1016            }
1017            edges.len().hash(state);
1018            for v in edges.iter() {
1019                hash_value(v, state);
1020            }
1021        }
1022        Value::GCounter(counts) => {
1023            // Sort keys for deterministic hash order.
1024            let mut pairs: Vec<_> = counts.iter().collect();
1025            pairs.sort_by_key(|(k, _)| k.as_str());
1026            pairs.len().hash(state);
1027            for (k, v) in pairs {
1028                k.hash(state);
1029                v.hash(state);
1030            }
1031        }
1032        Value::OnCounter { pos, neg } => {
1033            let mut pos_pairs: Vec<_> = pos.iter().collect();
1034            pos_pairs.sort_by_key(|(k, _)| k.as_str());
1035            pos_pairs.len().hash(state);
1036            for (k, v) in pos_pairs {
1037                k.hash(state);
1038                v.hash(state);
1039            }
1040            let mut neg_pairs: Vec<_> = neg.iter().collect();
1041            neg_pairs.sort_by_key(|(k, _)| k.as_str());
1042            neg_pairs.len().hash(state);
1043            for (k, v) in neg_pairs {
1044                k.hash(state);
1045                v.hash(state);
1046            }
1047        }
1048    }
1049}
1050
1051impl Hash for HashableValue {
1052    fn hash<H: Hasher>(&self, state: &mut H) {
1053        hash_value(&self.0, state);
1054    }
1055}
1056
1057/// Compares two `Value`s for hashable equality by reference (bit-equal floats).
1058fn values_hash_eq(a: &Value, b: &Value) -> bool {
1059    match (a, b) {
1060        (Value::Float64(a), Value::Float64(b)) => a.to_bits() == b.to_bits(),
1061        (Value::List(a), Value::List(b)) => {
1062            a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| values_hash_eq(x, y))
1063        }
1064        (Value::Map(a), Value::Map(b)) => {
1065            a.len() == b.len()
1066                && a.iter()
1067                    .all(|(k, v)| b.get(k).is_some_and(|bv| values_hash_eq(v, bv)))
1068        }
1069        (Value::Vector(a), Value::Vector(b)) => {
1070            a.len() == b.len()
1071                && a.iter()
1072                    .zip(b.iter())
1073                    .all(|(x, y)| x.to_bits() == y.to_bits())
1074        }
1075        (
1076            Value::Path {
1077                nodes: an,
1078                edges: ae,
1079            },
1080            Value::Path {
1081                nodes: bn,
1082                edges: be,
1083            },
1084        ) => {
1085            an.len() == bn.len()
1086                && ae.len() == be.len()
1087                && an.iter().zip(bn.iter()).all(|(x, y)| values_hash_eq(x, y))
1088                && ae.iter().zip(be.iter()).all(|(x, y)| values_hash_eq(x, y))
1089        }
1090        _ => a == b,
1091    }
1092}
1093
1094impl PartialEq for HashableValue {
1095    fn eq(&self, other: &Self) -> bool {
1096        values_hash_eq(&self.0, &other.0)
1097    }
1098}
1099
1100impl Eq for HashableValue {}
1101
1102impl From<Value> for HashableValue {
1103    fn from(value: Value) -> Self {
1104        Self(value)
1105    }
1106}
1107
1108impl From<HashableValue> for Value {
1109    fn from(hv: HashableValue) -> Self {
1110        hv.0
1111    }
1112}
1113
1114#[cfg(test)]
1115mod tests {
1116    use super::*;
1117
1118    #[test]
1119    fn test_value_type_checks() {
1120        assert!(Value::Null.is_null());
1121        assert!(!Value::Bool(true).is_null());
1122
1123        assert_eq!(Value::Bool(true).as_bool(), Some(true));
1124        assert_eq!(Value::Bool(false).as_bool(), Some(false));
1125        assert_eq!(Value::Int64(42).as_bool(), None);
1126
1127        assert_eq!(Value::Int64(42).as_int64(), Some(42));
1128        assert_eq!(Value::String("test".into()).as_int64(), None);
1129
1130        assert_eq!(Value::Float64(1.234).as_float64(), Some(1.234));
1131        assert_eq!(Value::String("hello".into()).as_str(), Some("hello"));
1132    }
1133
1134    #[test]
1135    fn test_value_from_conversions() {
1136        let v: Value = true.into();
1137        assert_eq!(v.as_bool(), Some(true));
1138
1139        let v: Value = 42i64.into();
1140        assert_eq!(v.as_int64(), Some(42));
1141
1142        let v: Value = 1.234f64.into();
1143        assert_eq!(v.as_float64(), Some(1.234));
1144
1145        let v: Value = "hello".into();
1146        assert_eq!(v.as_str(), Some("hello"));
1147
1148        let v: Value = vec![1u8, 2, 3].into();
1149        assert_eq!(v.as_bytes(), Some(&[1u8, 2, 3][..]));
1150    }
1151
1152    #[test]
1153    fn test_value_serialization_roundtrip() {
1154        let values = vec![
1155            Value::Null,
1156            Value::Bool(true),
1157            Value::Int64(i64::MAX),
1158            Value::Int64(i64::MIN),
1159            Value::Int64(0),
1160            Value::Float64(std::f64::consts::PI),
1161            Value::String("hello world".into()),
1162            Value::Bytes(vec![0, 1, 2, 255].into()),
1163            Value::List(vec![Value::Int64(1), Value::Int64(2)].into()),
1164        ];
1165
1166        for v in values {
1167            let bytes = v.serialize().unwrap();
1168            let decoded = Value::deserialize(&bytes).unwrap();
1169            assert_eq!(v, decoded);
1170        }
1171    }
1172
1173    #[test]
1174    fn test_property_key() {
1175        let key = PropertyKey::new("name");
1176        assert_eq!(key.as_str(), "name");
1177
1178        let key2: PropertyKey = "age".into();
1179        assert_eq!(key2.as_str(), "age");
1180
1181        // Keys should be comparable ("age" < "name" alphabetically)
1182        assert!(key2 < key);
1183    }
1184
1185    #[test]
1186    fn test_value_type_name() {
1187        assert_eq!(Value::Null.type_name(), "NULL");
1188        assert_eq!(Value::Bool(true).type_name(), "BOOL");
1189        assert_eq!(Value::Int64(0).type_name(), "INT64");
1190        assert_eq!(Value::Float64(0.0).type_name(), "FLOAT64");
1191        assert_eq!(Value::String("".into()).type_name(), "STRING");
1192        assert_eq!(Value::Bytes(vec![].into()).type_name(), "BYTES");
1193        assert_eq!(
1194            Value::Date(Date::from_ymd(2024, 1, 15).unwrap()).type_name(),
1195            "DATE"
1196        );
1197        assert_eq!(
1198            Value::Time(Time::from_hms(12, 0, 0).unwrap()).type_name(),
1199            "TIME"
1200        );
1201        assert_eq!(Value::Duration(Duration::default()).type_name(), "DURATION");
1202        assert_eq!(Value::List(vec![].into()).type_name(), "LIST");
1203        assert_eq!(Value::Map(BTreeMap::new().into()).type_name(), "MAP");
1204        assert_eq!(Value::Vector(vec![].into()).type_name(), "VECTOR");
1205    }
1206
1207    #[test]
1208    fn test_value_vector() {
1209        // Create vector directly (Vec<f32>.into() would create List due to generic impl)
1210        let v = Value::Vector(vec![0.1f32, 0.2, 0.3].into());
1211        assert!(v.is_vector());
1212        assert_eq!(v.vector_dimensions(), Some(3));
1213        assert_eq!(v.as_vector(), Some(&[0.1f32, 0.2, 0.3][..]));
1214
1215        // From slice
1216        let slice: &[f32] = &[1.0, 2.0, 3.0, 4.0];
1217        let v2: Value = slice.into();
1218        assert!(v2.is_vector());
1219        assert_eq!(v2.vector_dimensions(), Some(4));
1220
1221        // From Arc<[f32]>
1222        let arc: Arc<[f32]> = vec![5.0f32, 6.0].into();
1223        let v3: Value = arc.into();
1224        assert!(v3.is_vector());
1225        assert_eq!(v3.vector_dimensions(), Some(2));
1226
1227        // Non-vector returns None
1228        assert!(!Value::Int64(42).is_vector());
1229        assert_eq!(Value::Int64(42).as_vector(), None);
1230        assert_eq!(Value::Int64(42).vector_dimensions(), None);
1231    }
1232
1233    #[test]
1234    fn test_hashable_value_vector() {
1235        use std::collections::HashMap;
1236
1237        let mut map: HashMap<HashableValue, i32> = HashMap::new();
1238
1239        let v1 = HashableValue::new(Value::Vector(vec![0.1f32, 0.2, 0.3].into()));
1240        let v2 = HashableValue::new(Value::Vector(vec![0.1f32, 0.2, 0.3].into()));
1241        let v3 = HashableValue::new(Value::Vector(vec![0.4f32, 0.5, 0.6].into()));
1242
1243        map.insert(v1.clone(), 1);
1244
1245        // Same vector should hash to same bucket
1246        assert_eq!(map.get(&v2), Some(&1));
1247
1248        // Different vector should not match
1249        assert_eq!(map.get(&v3), None);
1250
1251        // v1 and v2 should be equal
1252        assert_eq!(v1, v2);
1253        assert_ne!(v1, v3);
1254    }
1255
1256    #[test]
1257    fn test_orderable_value_vector_unsupported() {
1258        // Vectors don't have a natural ordering, so try_from should return Err
1259        let v = Value::Vector(vec![0.1f32, 0.2, 0.3].into());
1260        assert!(OrderableValue::try_from(&v).is_err());
1261    }
1262
1263    #[test]
1264    fn test_hashable_value_basic() {
1265        use std::collections::HashMap;
1266
1267        let mut map: HashMap<HashableValue, i32> = HashMap::new();
1268
1269        // Test various value types as keys
1270        map.insert(HashableValue::new(Value::Int64(42)), 1);
1271        map.insert(HashableValue::new(Value::String("test".into())), 2);
1272        map.insert(HashableValue::new(Value::Bool(true)), 3);
1273        map.insert(HashableValue::new(Value::Float64(std::f64::consts::PI)), 4);
1274
1275        assert_eq!(map.get(&HashableValue::new(Value::Int64(42))), Some(&1));
1276        assert_eq!(
1277            map.get(&HashableValue::new(Value::String("test".into()))),
1278            Some(&2)
1279        );
1280        assert_eq!(map.get(&HashableValue::new(Value::Bool(true))), Some(&3));
1281        assert_eq!(
1282            map.get(&HashableValue::new(Value::Float64(std::f64::consts::PI))),
1283            Some(&4)
1284        );
1285    }
1286
1287    #[test]
1288    fn test_hashable_value_float_edge_cases() {
1289        use std::collections::HashMap;
1290
1291        let mut map: HashMap<HashableValue, i32> = HashMap::new();
1292
1293        // NaN should be hashable and equal to itself (same bits)
1294        let nan = f64::NAN;
1295        map.insert(HashableValue::new(Value::Float64(nan)), 1);
1296        assert_eq!(map.get(&HashableValue::new(Value::Float64(nan))), Some(&1));
1297
1298        // Positive and negative zero have different bits
1299        let pos_zero = 0.0f64;
1300        let neg_zero = -0.0f64;
1301        map.insert(HashableValue::new(Value::Float64(pos_zero)), 2);
1302        map.insert(HashableValue::new(Value::Float64(neg_zero)), 3);
1303        assert_eq!(
1304            map.get(&HashableValue::new(Value::Float64(pos_zero))),
1305            Some(&2)
1306        );
1307        assert_eq!(
1308            map.get(&HashableValue::new(Value::Float64(neg_zero))),
1309            Some(&3)
1310        );
1311    }
1312
1313    #[test]
1314    fn test_hashable_value_equality() {
1315        let v1 = HashableValue::new(Value::Int64(42));
1316        let v2 = HashableValue::new(Value::Int64(42));
1317        let v3 = HashableValue::new(Value::Int64(43));
1318
1319        assert_eq!(v1, v2);
1320        assert_ne!(v1, v3);
1321    }
1322
1323    #[test]
1324    fn test_hashable_value_inner() {
1325        let hv = HashableValue::new(Value::String("hello".into()));
1326        assert_eq!(hv.inner().as_str(), Some("hello"));
1327
1328        let v = hv.into_inner();
1329        assert_eq!(v.as_str(), Some("hello"));
1330    }
1331
1332    #[test]
1333    fn test_hashable_value_conversions() {
1334        let v = Value::Int64(42);
1335        let hv: HashableValue = v.clone().into();
1336        let v2: Value = hv.into();
1337        assert_eq!(v, v2);
1338    }
1339
1340    #[test]
1341    fn test_orderable_value_try_from() {
1342        // Supported types
1343        assert!(OrderableValue::try_from(&Value::Int64(42)).is_ok());
1344        assert!(OrderableValue::try_from(&Value::Float64(std::f64::consts::PI)).is_ok());
1345        assert!(OrderableValue::try_from(&Value::String("test".into())).is_ok());
1346        assert!(OrderableValue::try_from(&Value::Bool(true)).is_ok());
1347        assert!(OrderableValue::try_from(&Value::Timestamp(Timestamp::from_secs(1000))).is_ok());
1348        assert!(
1349            OrderableValue::try_from(&Value::Date(Date::from_ymd(2024, 1, 15).unwrap())).is_ok()
1350        );
1351        assert!(OrderableValue::try_from(&Value::Time(Time::from_hms(12, 0, 0).unwrap())).is_ok());
1352
1353        // Unsupported types
1354        assert!(OrderableValue::try_from(&Value::Null).is_err());
1355        assert!(OrderableValue::try_from(&Value::Bytes(vec![1, 2, 3].into())).is_err());
1356        assert!(OrderableValue::try_from(&Value::Duration(Duration::default())).is_err());
1357        assert!(OrderableValue::try_from(&Value::List(vec![].into())).is_err());
1358        assert!(OrderableValue::try_from(&Value::Map(BTreeMap::new().into())).is_err());
1359    }
1360
1361    #[test]
1362    fn test_orderable_value_ordering() {
1363        use std::collections::BTreeSet;
1364
1365        // Test integer ordering including i64::MIN and i64::MAX
1366        let mut set = BTreeSet::new();
1367        set.insert(OrderableValue::try_from(&Value::Int64(30)).unwrap());
1368        set.insert(OrderableValue::try_from(&Value::Int64(10)).unwrap());
1369        set.insert(OrderableValue::try_from(&Value::Int64(20)).unwrap());
1370        set.insert(OrderableValue::try_from(&Value::Int64(i64::MIN)).unwrap());
1371        set.insert(OrderableValue::try_from(&Value::Int64(i64::MAX)).unwrap());
1372
1373        let values: Vec<_> = set.iter().filter_map(|v| v.as_i64()).collect();
1374        assert_eq!(values, vec![i64::MIN, 10, 20, 30, i64::MAX]);
1375    }
1376
1377    #[test]
1378    fn test_orderable_value_float_ordering() {
1379        let v1 = OrderableValue::try_from(&Value::Float64(1.0)).unwrap();
1380        let v2 = OrderableValue::try_from(&Value::Float64(2.0)).unwrap();
1381        let v_nan = OrderableValue::try_from(&Value::Float64(f64::NAN)).unwrap();
1382        let v_inf = OrderableValue::try_from(&Value::Float64(f64::INFINITY)).unwrap();
1383
1384        assert!(v1 < v2);
1385        assert!(v2 < v_inf);
1386        assert!(v_inf < v_nan); // NaN is greater than everything
1387        assert!(v_nan == v_nan); // NaN equals itself for total ordering
1388    }
1389
1390    #[test]
1391    fn test_orderable_value_string_ordering() {
1392        let a = OrderableValue::try_from(&Value::String("apple".into())).unwrap();
1393        let b = OrderableValue::try_from(&Value::String("banana".into())).unwrap();
1394        let c = OrderableValue::try_from(&Value::String("cherry".into())).unwrap();
1395
1396        assert!(a < b);
1397        assert!(b < c);
1398    }
1399
1400    #[test]
1401    fn test_orderable_value_into_value() {
1402        let original = Value::Int64(42);
1403        let orderable = OrderableValue::try_from(&original).unwrap();
1404        let back = orderable.into_value();
1405        assert_eq!(original, back);
1406
1407        let original = Value::Float64(std::f64::consts::PI);
1408        let orderable = OrderableValue::try_from(&original).unwrap();
1409        let back = orderable.into_value();
1410        assert_eq!(original, back);
1411
1412        let original = Value::String("test".into());
1413        let orderable = OrderableValue::try_from(&original).unwrap();
1414        let back = orderable.into_value();
1415        assert_eq!(original, back);
1416    }
1417
1418    #[test]
1419    fn test_orderable_value_cross_type_numeric() {
1420        // Int64 and Float64 should be comparable
1421        let i = OrderableValue::try_from(&Value::Int64(10)).unwrap();
1422        let f = OrderableValue::try_from(&Value::Float64(10.0)).unwrap();
1423
1424        // They should compare as equal
1425        assert_eq!(i, f);
1426
1427        let f2 = OrderableValue::try_from(&Value::Float64(10.5)).unwrap();
1428        assert!(i < f2);
1429    }
1430
1431    #[test]
1432    fn test_ordered_float64_nan_handling() {
1433        let nan1 = OrderedFloat64::new(f64::NAN);
1434        let nan2 = OrderedFloat64::new(f64::NAN);
1435        let inf = OrderedFloat64::new(f64::INFINITY);
1436        let neg_inf = OrderedFloat64::new(f64::NEG_INFINITY);
1437        let zero = OrderedFloat64::new(0.0);
1438
1439        // NaN equals itself
1440        assert_eq!(nan1, nan2);
1441
1442        // Ordering: -inf < 0 < inf < nan
1443        assert!(neg_inf < zero);
1444        assert!(zero < inf);
1445        assert!(inf < nan1);
1446    }
1447
1448    #[test]
1449    fn test_value_temporal_accessors() {
1450        let date = Date::from_ymd(2024, 3, 15).unwrap();
1451        let time = Time::from_hms(14, 30, 0).unwrap();
1452        let dur = Duration::from_months(3);
1453
1454        let vd = Value::Date(date);
1455        let vt = Value::Time(time);
1456        let vr = Value::Duration(dur);
1457
1458        assert_eq!(vd.as_date(), Some(date));
1459        assert_eq!(vt.as_time(), Some(time));
1460        assert_eq!(vr.as_duration(), Some(dur));
1461
1462        // Wrong type returns None
1463        assert_eq!(vd.as_time(), None);
1464        assert_eq!(vt.as_date(), None);
1465        assert_eq!(vd.as_duration(), None);
1466    }
1467
1468    #[test]
1469    fn test_value_temporal_from_conversions() {
1470        let date = Date::from_ymd(2024, 1, 15).unwrap();
1471        let v: Value = date.into();
1472        assert_eq!(v.as_date(), Some(date));
1473
1474        let time = Time::from_hms(10, 30, 0).unwrap();
1475        let v: Value = time.into();
1476        assert_eq!(v.as_time(), Some(time));
1477
1478        let dur = Duration::from_days(7);
1479        let v: Value = dur.into();
1480        assert_eq!(v.as_duration(), Some(dur));
1481    }
1482
1483    #[test]
1484    fn test_value_temporal_display() {
1485        let v = Value::Date(Date::from_ymd(2024, 3, 15).unwrap());
1486        assert_eq!(format!("{v}"), "2024-03-15");
1487
1488        let v = Value::Time(Time::from_hms(14, 30, 0).unwrap());
1489        assert_eq!(format!("{v}"), "14:30:00");
1490
1491        let v = Value::Duration(Duration::from_days(7));
1492        assert_eq!(format!("{v}"), "P7D");
1493    }
1494
1495    #[test]
1496    fn test_value_temporal_serialization_roundtrip() {
1497        let values = vec![
1498            Value::Date(Date::from_ymd(2024, 6, 15).unwrap()),
1499            Value::Time(Time::from_hms(23, 59, 59).unwrap()),
1500            Value::Duration(Duration::new(1, 2, 3_000_000_000)),
1501        ];
1502
1503        for v in values {
1504            let bytes = v.serialize().unwrap();
1505            let decoded = Value::deserialize(&bytes).unwrap();
1506            assert_eq!(v, decoded);
1507        }
1508    }
1509
1510    #[test]
1511    fn test_orderable_value_date_ordering() {
1512        let d1 =
1513            OrderableValue::try_from(&Value::Date(Date::from_ymd(2024, 1, 1).unwrap())).unwrap();
1514        let d2 =
1515            OrderableValue::try_from(&Value::Date(Date::from_ymd(2024, 6, 15).unwrap())).unwrap();
1516        assert!(d1 < d2);
1517
1518        let back = d1.into_value();
1519        assert_eq!(back.as_date(), Some(Date::from_ymd(2024, 1, 1).unwrap()));
1520    }
1521
1522    #[test]
1523    fn test_hashable_value_temporal() {
1524        use std::collections::HashMap;
1525
1526        let mut map: HashMap<HashableValue, i32> = HashMap::new();
1527
1528        let date_val = Value::Date(Date::from_ymd(2024, 3, 15).unwrap());
1529        map.insert(HashableValue::new(date_val.clone()), 1);
1530        assert_eq!(map.get(&HashableValue::new(date_val)), Some(&1));
1531
1532        let time_val = Value::Time(Time::from_hms(12, 0, 0).unwrap());
1533        map.insert(HashableValue::new(time_val.clone()), 2);
1534        assert_eq!(map.get(&HashableValue::new(time_val)), Some(&2));
1535
1536        let dur_val = Value::Duration(Duration::from_months(6));
1537        map.insert(HashableValue::new(dur_val.clone()), 3);
1538        assert_eq!(map.get(&HashableValue::new(dur_val)), Some(&3));
1539    }
1540
1541    // --- T2-15: Value::Path tests ---
1542
1543    #[test]
1544    fn test_value_path_construction_and_equality() {
1545        let path1 = Value::Path {
1546            nodes: vec![Value::Int64(1), Value::Int64(2), Value::Int64(3)].into(),
1547            edges: vec![Value::String("KNOWS".into()), Value::String("LIKES".into())].into(),
1548        };
1549        let path2 = Value::Path {
1550            nodes: vec![Value::Int64(1), Value::Int64(2), Value::Int64(3)].into(),
1551            edges: vec![Value::String("KNOWS".into()), Value::String("LIKES".into())].into(),
1552        };
1553        let path3 = Value::Path {
1554            nodes: vec![Value::Int64(1), Value::Int64(2)].into(),
1555            edges: vec![Value::String("KNOWS".into())].into(),
1556        };
1557
1558        assert_eq!(path1, path2, "Identical paths should be equal");
1559        assert_ne!(path1, path3, "Different paths should not be equal");
1560    }
1561
1562    #[test]
1563    fn test_value_path_type_name() {
1564        let path = Value::Path {
1565            nodes: vec![Value::Int64(1)].into(),
1566            edges: vec![].into(),
1567        };
1568        assert_eq!(path.type_name(), "PATH");
1569    }
1570
1571    #[test]
1572    fn test_value_path_serialization_roundtrip() {
1573        let path = Value::Path {
1574            nodes: vec![
1575                Value::String("node_a".into()),
1576                Value::String("node_b".into()),
1577            ]
1578            .into(),
1579            edges: vec![Value::String("CONNECTS".into())].into(),
1580        };
1581
1582        let bytes = path.serialize().unwrap();
1583        let decoded = Value::deserialize(&bytes).unwrap();
1584        assert_eq!(path, decoded);
1585    }
1586
1587    #[test]
1588    fn test_value_path_hashing() {
1589        use std::collections::HashMap;
1590
1591        let path1 = Value::Path {
1592            nodes: vec![Value::Int64(1), Value::Int64(2)].into(),
1593            edges: vec![Value::String("KNOWS".into())].into(),
1594        };
1595        let path2 = Value::Path {
1596            nodes: vec![Value::Int64(1), Value::Int64(2)].into(),
1597            edges: vec![Value::String("KNOWS".into())].into(),
1598        };
1599
1600        let mut map: HashMap<HashableValue, i32> = HashMap::new();
1601        map.insert(HashableValue::new(path1), 42);
1602        assert_eq!(map.get(&HashableValue::new(path2)), Some(&42));
1603    }
1604
1605    // --- T2-16: Collection nesting tests ---
1606
1607    #[test]
1608    fn test_nested_list_serialization_roundtrip() {
1609        let nested = Value::List(
1610            vec![
1611                Value::List(vec![Value::Int64(1), Value::Int64(2)].into()),
1612                Value::List(vec![Value::Int64(3), Value::Int64(4)].into()),
1613            ]
1614            .into(),
1615        );
1616
1617        let bytes = nested.serialize().unwrap();
1618        let decoded = Value::deserialize(&bytes).unwrap();
1619        assert_eq!(nested, decoded);
1620    }
1621
1622    #[test]
1623    fn test_map_with_entries_serialization_roundtrip() {
1624        let mut entries = BTreeMap::new();
1625        entries.insert(PropertyKey::new("name"), Value::String("Alix".into()));
1626        entries.insert(PropertyKey::new("age"), Value::Int64(30));
1627        entries.insert(PropertyKey::new("active"), Value::Bool(true));
1628
1629        let map = Value::Map(entries.into());
1630
1631        let bytes = map.serialize().unwrap();
1632        let decoded = Value::deserialize(&bytes).unwrap();
1633        assert_eq!(map, decoded);
1634    }
1635
1636    #[test]
1637    fn test_mixed_type_list_serialization_roundtrip() {
1638        let mixed = Value::List(
1639            vec![
1640                Value::Int64(1),
1641                Value::String("hello".into()),
1642                Value::Null,
1643                Value::Bool(false),
1644                Value::Float64(3.125),
1645            ]
1646            .into(),
1647        );
1648
1649        let bytes = mixed.serialize().unwrap();
1650        let decoded = Value::deserialize(&bytes).unwrap();
1651        assert_eq!(mixed, decoded);
1652    }
1653
1654    #[test]
1655    fn test_map_with_nested_list() {
1656        let mut entries = BTreeMap::new();
1657        entries.insert(
1658            PropertyKey::new("tags"),
1659            Value::List(vec![Value::String("a".into()), Value::String("b".into())].into()),
1660        );
1661        entries.insert(PropertyKey::new("count"), Value::Int64(2));
1662
1663        let map = Value::Map(entries.into());
1664
1665        let bytes = map.serialize().unwrap();
1666        let decoded = Value::deserialize(&bytes).unwrap();
1667        assert_eq!(map, decoded);
1668    }
1669
1670    #[test]
1671    fn test_list_with_nested_map() {
1672        let mut inner_map = BTreeMap::new();
1673        inner_map.insert(PropertyKey::new("key"), Value::String("val".into()));
1674
1675        let list = Value::List(vec![Value::Map(inner_map.into()), Value::Int64(42)].into());
1676
1677        let bytes = list.serialize().unwrap();
1678        let decoded = Value::deserialize(&bytes).unwrap();
1679        assert_eq!(list, decoded);
1680    }
1681
1682    #[test]
1683    fn test_property_key_borrow_str() {
1684        use std::collections::HashMap;
1685        let mut map: HashMap<PropertyKey, i32> = HashMap::new();
1686        map.insert(PropertyKey::new("name"), 42);
1687        map.insert(PropertyKey::new("age"), 30);
1688        assert_eq!(map.get("name"), Some(&42));
1689        assert_eq!(map.get("age"), Some(&30));
1690        assert_eq!(map.get("missing"), None);
1691        assert!(map.contains_key("name"));
1692        assert!(!map.contains_key("nope"));
1693    }
1694
1695    #[test]
1696    fn test_gcounter_type_name() {
1697        let v = Value::GCounter(Arc::new(std::collections::HashMap::new()));
1698        assert_eq!(v.type_name(), "GCOUNTER");
1699    }
1700
1701    #[test]
1702    fn test_oncounter_type_name() {
1703        let v = Value::OnCounter {
1704            pos: Arc::new(std::collections::HashMap::new()),
1705            neg: Arc::new(std::collections::HashMap::new()),
1706        };
1707        assert_eq!(v.type_name(), "PNCOUNTER");
1708    }
1709
1710    #[test]
1711    fn test_gcounter_display() {
1712        let mut counts = std::collections::HashMap::new();
1713        counts.insert("node-a".to_string(), 10u64);
1714        counts.insert("node-b".to_string(), 5u64);
1715        let v = Value::GCounter(Arc::new(counts));
1716        assert_eq!(format!("{v}"), "GCounter(15)");
1717    }
1718
1719    #[test]
1720    fn test_gcounter_debug() {
1721        let mut counts = std::collections::HashMap::new();
1722        counts.insert("node-a".to_string(), 3u64);
1723        let v = Value::GCounter(Arc::new(counts));
1724        let debug = format!("{v:?}");
1725        assert!(debug.contains("GCounter"));
1726        assert!(debug.contains("total=3"));
1727    }
1728
1729    #[test]
1730    fn test_oncounter_display() {
1731        let mut pos = std::collections::HashMap::new();
1732        pos.insert("node-a".to_string(), 10u64);
1733        pos.insert("node-b".to_string(), 3u64);
1734        let mut neg = std::collections::HashMap::new();
1735        neg.insert("node-a".to_string(), 4u64);
1736        let v = Value::OnCounter {
1737            pos: Arc::new(pos),
1738            neg: Arc::new(neg),
1739        };
1740        // pos_sum = 13, neg_sum = 4, net = 9
1741        assert_eq!(format!("{v}"), "OnCounter(9)");
1742    }
1743
1744    #[test]
1745    fn test_oncounter_debug() {
1746        let v = Value::OnCounter {
1747            pos: Arc::new(std::collections::HashMap::new()),
1748            neg: Arc::new(std::collections::HashMap::new()),
1749        };
1750        let debug = format!("{v:?}");
1751        assert!(debug.contains("OnCounter"));
1752        assert!(debug.contains("net=0"));
1753    }
1754
1755    #[test]
1756    fn test_gcounter_hash_is_insertion_order_independent() {
1757        use std::hash::{Hash, Hasher};
1758        let mut counts1 = std::collections::HashMap::new();
1759        counts1.insert("b".to_string(), 2u64);
1760        counts1.insert("a".to_string(), 1u64);
1761        let mut counts2 = std::collections::HashMap::new();
1762        counts2.insert("a".to_string(), 1u64);
1763        counts2.insert("b".to_string(), 2u64);
1764        let v1 = HashableValue(Value::GCounter(Arc::new(counts1)));
1765        let v2 = HashableValue(Value::GCounter(Arc::new(counts2)));
1766        let mut h1 = std::collections::hash_map::DefaultHasher::new();
1767        let mut h2 = std::collections::hash_map::DefaultHasher::new();
1768        v1.hash(&mut h1);
1769        v2.hash(&mut h2);
1770        assert_eq!(h1.finish(), h2.finish());
1771    }
1772
1773    #[test]
1774    fn test_oncounter_hash_is_insertion_order_independent() {
1775        use std::hash::{Hash, Hasher};
1776        let mut pos1 = std::collections::HashMap::new();
1777        pos1.insert("b".to_string(), 5u64);
1778        pos1.insert("a".to_string(), 3u64);
1779        let mut pos2 = std::collections::HashMap::new();
1780        pos2.insert("a".to_string(), 3u64);
1781        pos2.insert("b".to_string(), 5u64);
1782        let neg = Arc::new(std::collections::HashMap::new());
1783        let v1 = HashableValue(Value::OnCounter {
1784            pos: Arc::new(pos1),
1785            neg: neg.clone(),
1786        });
1787        let v2 = HashableValue(Value::OnCounter {
1788            pos: Arc::new(pos2),
1789            neg: neg.clone(),
1790        });
1791        let mut h1 = std::collections::hash_map::DefaultHasher::new();
1792        let mut h2 = std::collections::hash_map::DefaultHasher::new();
1793        v1.hash(&mut h1);
1794        v2.hash(&mut h2);
1795        assert_eq!(h1.finish(), h2.finish());
1796    }
1797
1798    #[test]
1799    fn test_gcounter_serialize_roundtrip() {
1800        let mut counts = std::collections::HashMap::new();
1801        counts.insert("replica-1".to_string(), 42u64);
1802        counts.insert("replica-2".to_string(), 7u64);
1803        let v = Value::GCounter(Arc::new(counts));
1804        let bytes = v.serialize().unwrap();
1805        let decoded = Value::deserialize(&bytes).unwrap();
1806        assert_eq!(v, decoded);
1807    }
1808
1809    #[test]
1810    fn test_oncounter_serialize_roundtrip() {
1811        let mut pos = std::collections::HashMap::new();
1812        pos.insert("node-a".to_string(), 10u64);
1813        let mut neg = std::collections::HashMap::new();
1814        neg.insert("node-a".to_string(), 3u64);
1815        let v = Value::OnCounter {
1816            pos: Arc::new(pos),
1817            neg: Arc::new(neg),
1818        };
1819        let bytes = v.serialize().unwrap();
1820        let decoded = Value::deserialize(&bytes).unwrap();
1821        assert_eq!(v, decoded);
1822    }
1823
1824    #[test]
1825    fn test_gcounter_empty_display() {
1826        let v = Value::GCounter(Arc::new(std::collections::HashMap::new()));
1827        assert_eq!(format!("{v}"), "GCounter(0)");
1828    }
1829
1830    #[test]
1831    fn test_oncounter_zero_net() {
1832        let mut pos = std::collections::HashMap::new();
1833        pos.insert("r".to_string(), 5u64);
1834        let mut neg = std::collections::HashMap::new();
1835        neg.insert("r".to_string(), 5u64);
1836        let v = Value::OnCounter {
1837            pos: Arc::new(pos),
1838            neg: Arc::new(neg),
1839        };
1840        assert_eq!(format!("{v}"), "OnCounter(0)");
1841    }
1842
1843    #[test]
1844    fn test_estimated_size_bytes_fixed_types() {
1845        assert_eq!(Value::Null.estimated_size_bytes(), 0);
1846        assert_eq!(Value::Bool(true).estimated_size_bytes(), 0);
1847        assert_eq!(Value::Int64(42).estimated_size_bytes(), 0);
1848        assert_eq!(Value::Float64(3.125).estimated_size_bytes(), 0);
1849    }
1850
1851    #[test]
1852    fn test_estimated_size_bytes_string() {
1853        let v = Value::from("hello");
1854        assert_eq!(v.estimated_size_bytes(), 5);
1855    }
1856
1857    #[test]
1858    fn test_estimated_size_bytes_bytes() {
1859        let v = Value::Bytes(Arc::from(vec![0u8; 100].as_slice()));
1860        assert_eq!(v.estimated_size_bytes(), 100);
1861    }
1862
1863    #[test]
1864    fn test_estimated_size_bytes_vector() {
1865        let v = Value::Vector(Arc::from(vec![1.0f32; 384].as_slice()));
1866        assert_eq!(v.estimated_size_bytes(), 384 * 4);
1867    }
1868
1869    #[test]
1870    fn test_estimated_size_bytes_list() {
1871        let v = Value::List(Arc::from(vec![Value::from("abc"), Value::Int64(1)]));
1872        assert!(v.estimated_size_bytes() >= 3);
1873    }
1874
1875    // --- Accessor tests for temporal and counter types ---
1876
1877    #[test]
1878    fn test_as_date_matching() {
1879        let date = Date::from_ymd(2024, 6, 15).unwrap();
1880        let v = Value::Date(date);
1881        assert_eq!(v.as_date(), Some(date));
1882    }
1883
1884    #[test]
1885    fn test_as_date_non_matching() {
1886        let v = Value::Int64(42);
1887        assert_eq!(v.as_date(), None);
1888    }
1889
1890    #[test]
1891    fn test_as_time_matching() {
1892        let time = Time::from_hms(14, 30, 0).unwrap();
1893        let v = Value::Time(time);
1894        assert_eq!(v.as_time(), Some(time));
1895    }
1896
1897    #[test]
1898    fn test_as_time_non_matching() {
1899        let v = Value::String("x".into());
1900        assert_eq!(v.as_time(), None);
1901    }
1902
1903    #[test]
1904    fn test_as_duration_matching() {
1905        let dur = Duration::new(1, 2, 3_000_000_000);
1906        let v = Value::Duration(dur);
1907        assert_eq!(v.as_duration(), Some(dur));
1908    }
1909
1910    #[test]
1911    fn test_as_duration_non_matching() {
1912        let v = Value::Bool(true);
1913        assert_eq!(v.as_duration(), None);
1914    }
1915
1916    #[test]
1917    fn test_as_zoned_datetime_matching() {
1918        let zdt = ZonedDatetime::parse("2024-06-15T10:30:00+01:00").unwrap();
1919        let v = Value::ZonedDatetime(zdt);
1920        assert_eq!(v.as_zoned_datetime(), Some(zdt));
1921    }
1922
1923    #[test]
1924    fn test_as_zoned_datetime_non_matching() {
1925        let v = Value::Null;
1926        assert_eq!(v.as_zoned_datetime(), None);
1927    }
1928
1929    #[test]
1930    fn test_as_zoned_datetime_from_conversion() {
1931        let zdt = ZonedDatetime::parse("2025-01-01T00:00:00+02:00").unwrap();
1932        let v: Value = zdt.into();
1933        assert_eq!(v.as_zoned_datetime(), Some(zdt));
1934        assert_eq!(v.type_name(), "ZONED DATETIME");
1935    }
1936
1937    #[test]
1938    fn test_as_date_wrong_temporal_type() {
1939        // A Time value should not be returned by as_date
1940        let time = Time::from_hms(12, 0, 0).unwrap();
1941        let v = Value::Time(time);
1942        assert_eq!(v.as_date(), None);
1943    }
1944
1945    #[test]
1946    fn test_as_time_wrong_temporal_type() {
1947        // A Date value should not be returned by as_time
1948        let date = Date::from_ymd(2024, 3, 15).unwrap();
1949        let v = Value::Date(date);
1950        assert_eq!(v.as_time(), None);
1951    }
1952
1953    #[test]
1954    fn test_as_duration_wrong_temporal_type() {
1955        // A Timestamp should not be returned by as_duration
1956        let ts = Timestamp::from_secs(1_000_000);
1957        let v = Value::Timestamp(ts);
1958        assert_eq!(v.as_duration(), None);
1959    }
1960
1961    #[test]
1962    fn test_as_zoned_datetime_wrong_temporal_type() {
1963        // A plain Timestamp should not be returned by as_zoned_datetime
1964        let ts = Timestamp::from_secs(1_000_000);
1965        let v = Value::Timestamp(ts);
1966        assert_eq!(v.as_zoned_datetime(), None);
1967    }
1968
1969    #[test]
1970    fn test_zoned_datetime_serialization_roundtrip() {
1971        let zdt = ZonedDatetime::parse("2024-12-25T18:00:00+05:30").unwrap();
1972        let v = Value::ZonedDatetime(zdt);
1973        let bytes = v.serialize().unwrap();
1974        let decoded = Value::deserialize(&bytes).unwrap();
1975        assert_eq!(v, decoded);
1976        assert_eq!(decoded.as_zoned_datetime(), Some(zdt));
1977    }
1978
1979    #[test]
1980    fn test_zoned_datetime_type_name() {
1981        let zdt = ZonedDatetime::parse("2024-06-15T10:30:00+02:00").unwrap();
1982        let v = Value::ZonedDatetime(zdt);
1983        assert_eq!(v.type_name(), "ZONED DATETIME");
1984    }
1985
1986    #[test]
1987    fn test_zoned_datetime_display() {
1988        let zdt = ZonedDatetime::parse("2024-06-15T10:30:00+02:00").unwrap();
1989        let v = Value::ZonedDatetime(zdt);
1990        let displayed = format!("{v}");
1991        assert!(
1992            displayed.contains("2024-06-15"),
1993            "Display should contain the date"
1994        );
1995        assert!(
1996            displayed.contains("10:30:00"),
1997            "Display should contain the time"
1998        );
1999    }
2000}