datalogic_rs/value/
data_value.rs

1//! Core DataValue implementation.
2//!
3//! This module provides the DataValue enum, which is a memory-efficient
4//! representation of data values that leverages arena allocation.
5
6use super::number::NumberValue;
7use crate::arena::DataArena;
8use chrono::{DateTime, Duration, Utc};
9use std::cmp::Ordering;
10use std::fmt;
11
12/// A memory-efficient value type that leverages arena allocation.
13///
14/// This replaces the direct dependency on `serde_json::Value` with a custom
15/// implementation optimized for rule evaluation.
16#[derive(Debug, Clone, PartialEq)]
17pub enum DataValue<'a> {
18    /// Represents a null value
19    Null,
20
21    /// Represents a boolean value
22    Bool(bool),
23
24    /// Represents a numeric value (integer or floating point)
25    Number(NumberValue),
26
27    /// Represents a string value (arena-allocated)
28    String(&'a str),
29
30    /// Represents an array of values (arena-allocated)
31    Array(&'a [DataValue<'a>]),
32
33    /// Represents an object with key-value pairs (arena-allocated)
34    Object(&'a [(&'a str, DataValue<'a>)]),
35
36    /// Represents a datetime value
37    DateTime(DateTime<Utc>),
38
39    /// Represents a duration value
40    Duration(Duration),
41}
42
43impl<'a> DataValue<'a> {
44    /// Creates a null value.
45    pub fn null() -> Self {
46        DataValue::Null
47    }
48
49    /// Creates a boolean value.
50    pub fn bool(value: bool) -> Self {
51        DataValue::Bool(value)
52    }
53
54    /// Creates an integer value.
55    pub fn integer(value: i64) -> Self {
56        DataValue::Number(NumberValue::Integer(value))
57    }
58
59    /// Creates a floating-point value.
60    pub fn float(value: f64) -> Self {
61        DataValue::Number(NumberValue::from_f64(value))
62    }
63
64    /// Creates a string value.
65    ///
66    /// If the string is empty, returns a string value with the preallocated empty string.
67    pub fn string(arena: &'a DataArena, value: &str) -> Self {
68        if value.is_empty() {
69            // Use the preallocated empty string
70            DataValue::String(arena.empty_string())
71        } else {
72            DataValue::String(arena.alloc_str(value))
73        }
74    }
75
76    /// Creates a datetime value from a `chrono::DateTime<Utc>`.
77    pub fn datetime(value: DateTime<Utc>) -> Self {
78        DataValue::DateTime(value)
79    }
80
81    /// Creates a duration value from a `chrono::Duration`.
82    pub fn duration(value: Duration) -> Self {
83        DataValue::Duration(value)
84    }
85
86    /// Creates an array value.
87    ///
88    /// If the array is empty, returns a value with the preallocated empty array.
89    /// For small arrays (up to 8 elements), uses an optimized allocation method.
90    pub fn array(arena: &'a DataArena, values: &[DataValue<'a>]) -> Self {
91        if values.is_empty() {
92            // Use the preallocated empty array
93            DataValue::Array(arena.empty_array())
94        } else {
95            // Use the standard allocation for larger arrays
96            DataValue::Array(arena.alloc_data_value_slice(values))
97        }
98    }
99
100    /// Creates an object value.
101    ///
102    /// If the entries array is empty, returns an object with an empty entries array.
103    pub fn object(arena: &'a DataArena, entries: &[(&'a str, DataValue<'a>)]) -> Self {
104        DataValue::Object(arena.alloc_object_entries(entries))
105    }
106
107    /// Returns true if the value is null.
108    pub fn is_null(&self) -> bool {
109        matches!(self, DataValue::Null)
110    }
111
112    /// Returns true if the value is a boolean.
113    pub fn is_bool(&self) -> bool {
114        matches!(self, DataValue::Bool(_))
115    }
116
117    /// Returns true if the value is a number.
118    pub fn is_number(&self) -> bool {
119        matches!(self, DataValue::Number(_))
120    }
121
122    /// Returns true if the value is a string.
123    pub fn is_string(&self) -> bool {
124        matches!(self, DataValue::String(_))
125    }
126
127    /// Returns true if the value is an array.
128    pub fn is_array(&self) -> bool {
129        matches!(self, DataValue::Array(_))
130    }
131
132    /// Returns true if the value is an object.
133    pub fn is_object(&self) -> bool {
134        matches!(self, DataValue::Object(_))
135    }
136
137    /// Returns true if the value is a datetime.
138    pub fn is_datetime(&self) -> bool {
139        matches!(self, DataValue::DateTime(_))
140    }
141
142    /// Returns true if the value is a duration.
143    pub fn is_duration(&self) -> bool {
144        matches!(self, DataValue::Duration(_))
145    }
146
147    /// Returns the value as a boolean, if it is one.
148    pub fn as_bool(&self) -> Option<bool> {
149        match self {
150            DataValue::Bool(b) => Some(*b),
151            _ => None,
152        }
153    }
154
155    /// Returns the value as an i64, if it is a number that can be represented as an i64.
156    pub fn as_i64(&self) -> Option<i64> {
157        match self {
158            DataValue::Number(n) => n.as_i64(),
159            _ => None,
160        }
161    }
162
163    /// Returns the value as an f64, if it is a number.
164    pub fn as_f64(&self) -> Option<f64> {
165        match self {
166            DataValue::Number(n) => Some(n.as_f64()),
167            _ => None,
168        }
169    }
170
171    /// Returns the value as a string slice, if it is a string.
172    pub fn as_str(&self) -> Option<&str> {
173        match self {
174            DataValue::String(s) => Some(s),
175            _ => None,
176        }
177    }
178
179    /// Returns the value as an array slice, if it is an array.
180    pub fn as_array(&self) -> Option<&[DataValue<'a>]> {
181        match self {
182            DataValue::Array(a) => Some(a),
183            _ => None,
184        }
185    }
186
187    /// Returns the value as a datetime, if it is a datetime.
188    pub fn as_datetime(&self) -> Option<&DateTime<Utc>> {
189        match self {
190            DataValue::DateTime(dt) => Some(dt),
191            _ => None,
192        }
193    }
194
195    /// Returns the value as a duration, if it is a duration.
196    pub fn as_duration(&self) -> Option<&Duration> {
197        match self {
198            DataValue::Duration(d) => Some(d),
199            _ => None,
200        }
201    }
202
203    /// Returns the value as an object slice, if it is an object.
204    pub fn as_object(&self) -> Option<&[(&'a str, DataValue<'a>)]> {
205        match self {
206            DataValue::Object(o) => Some(o),
207            _ => None,
208        }
209    }
210
211    /// Coerces the value to a boolean.
212    ///
213    /// The coercion follows JSON Logic rules:
214    /// - `null` is `false`
215    /// - `false` is `false`
216    /// - Empty string is `false`
217    /// - Empty array is `false`
218    /// - Empty object is `false`
219    /// - `0` is `false`
220    /// - Everything else is `true`
221    #[inline]
222    pub fn coerce_to_bool(&self) -> bool {
223        match self {
224            // Fast path for common cases
225            DataValue::Bool(b) => *b,
226            DataValue::Null => false,
227
228            // Number case - only 0 is false
229            DataValue::Number(n) => {
230                // Fast path for integers
231                if let NumberValue::Integer(i) = n {
232                    *i != 0
233                } else {
234                    n.as_f64() != 0.0
235                }
236            }
237
238            // String case - only empty string is false
239            DataValue::String(s) => !s.is_empty(),
240
241            // Array case - only empty array is false
242            DataValue::Array(items) => !items.is_empty(),
243
244            // Object case - only empty object is false
245            DataValue::Object(items) => !items.is_empty(),
246
247            // DateTime always coerces to true
248            DataValue::DateTime(_) => true,
249
250            // Duration is false if zero
251            DataValue::Duration(d) => !d.is_zero(),
252        }
253    }
254
255    /// Coerces the value to a number according to JSONLogic rules.
256    #[inline]
257    pub fn coerce_to_number(&self) -> Option<NumberValue> {
258        match self {
259            // Fast paths for common cases
260            DataValue::Number(n) => Some(*n),
261            DataValue::Bool(b) => Some(NumberValue::Integer(if *b { 1 } else { 0 })),
262            DataValue::Null => Some(NumberValue::Integer(0)),
263
264            DataValue::String(s) => {
265                // Fast path for empty strings
266                if s.is_empty() {
267                    return Some(NumberValue::Integer(0));
268                }
269
270                // Fast path for simple integers
271                let mut is_integer = true;
272                let mut value: i64 = 0;
273                let mut negative = false;
274                let bytes = s.as_bytes();
275
276                // Check for negative sign
277                let mut i = 0;
278                if !bytes.is_empty() && bytes[0] == b'-' {
279                    negative = true;
280                    i = 1;
281                }
282
283                // Parse digits
284                while i < bytes.len() {
285                    let b = bytes[i];
286                    if b.is_ascii_digit() {
287                        // Check for overflow
288                        if value > i64::MAX / 10 {
289                            is_integer = false;
290                            break;
291                        }
292                        value = value * 10 + (b - b'0') as i64;
293                    } else {
294                        is_integer = false;
295                        break;
296                    }
297                    i += 1;
298                }
299
300                if is_integer {
301                    if negative {
302                        value = -value;
303                    }
304                    return Some(NumberValue::Integer(value));
305                }
306
307                // Fall back to standard parsing for more complex cases
308                if let Ok(i) = s.parse::<i64>() {
309                    Some(NumberValue::Integer(i))
310                } else if let Ok(f) = s.parse::<f64>() {
311                    Some(NumberValue::Float(f))
312                } else {
313                    None
314                }
315            }
316
317            // DateTime can be converted to Unix timestamp
318            DataValue::DateTime(dt) => {
319                let timestamp = dt.timestamp_millis() as f64 / 1000.0;
320                Some(NumberValue::Float(timestamp))
321            }
322
323            // Duration can be converted to total seconds
324            DataValue::Duration(d) => {
325                let total_seconds = d.num_seconds();
326                Some(NumberValue::Integer(total_seconds))
327            }
328
329            DataValue::Array(_) => None,
330
331            DataValue::Object(_) => None,
332        }
333    }
334
335    /// Coerces the value to a string according to JSONLogic rules.
336    pub fn coerce_to_string(&self, arena: &'a DataArena) -> DataValue<'a> {
337        match self {
338            DataValue::Null => DataValue::String(arena.alloc_str("null")),
339            DataValue::Bool(b) => {
340                DataValue::String(arena.alloc_str(if *b { "true" } else { "false" }))
341            }
342            DataValue::Number(n) => DataValue::String(arena.alloc_str(&n.to_string())),
343            DataValue::String(s) => DataValue::String(s),
344            DataValue::Array(a) => {
345                let mut result = String::new();
346                for (i, v) in a.iter().enumerate() {
347                    if i > 0 {
348                        result.push(',');
349                    }
350                    if let DataValue::String(s) = v.coerce_to_string(arena) {
351                        result.push_str(s);
352                    }
353                }
354                DataValue::String(arena.alloc_str(&result))
355            }
356            DataValue::Object(_) => DataValue::String(arena.alloc_str("[object Object]")),
357            DataValue::DateTime(dt) => {
358                // Format datetime in ISO 8601 format
359                let iso_string = dt.to_rfc3339();
360                DataValue::String(arena.alloc_str(&iso_string))
361            }
362            DataValue::Duration(d) => {
363                // Format duration as 1d:2h:3m:4s
364                let days = d.num_days();
365                let hours = d.num_hours() % 24;
366                let minutes = d.num_minutes() % 60;
367                let seconds = d.num_seconds() % 60;
368                let formatted = format!("{}d:{}h:{}m:{}s", days, hours, minutes, seconds);
369                DataValue::String(arena.alloc_str(&formatted))
370            }
371        }
372    }
373
374    /// Gets a value from an object by key.
375    pub fn get(&self, key: &str) -> Option<&DataValue<'a>> {
376        match self {
377            DataValue::Object(entries) => entries
378                .binary_search_by_key(&key, |&(k, _)| k)
379                .ok()
380                .map(|idx| &entries[idx].1),
381            _ => None,
382        }
383    }
384
385    /// Gets a value from an array by index.
386    pub fn get_index(&self, index: usize) -> Option<&DataValue<'a>> {
387        match self {
388            DataValue::Array(elements) => elements.get(index),
389            _ => None,
390        }
391    }
392
393    /// Returns a string representation of the type of this value.
394    pub fn type_name(&self) -> &'static str {
395        match self {
396            DataValue::Null => "null",
397            DataValue::Bool(_) => "boolean",
398            DataValue::Number(_) => "number",
399            DataValue::String(_) => "string",
400            DataValue::Array(_) => "array",
401            DataValue::Object(_) => "object",
402            DataValue::DateTime(_) => "datetime",
403            DataValue::Duration(_) => "duration",
404        }
405    }
406
407    /// Checks if this value equals another value, with type coercion.
408    pub fn equals(&self, other: &DataValue<'a>) -> bool {
409        match (self, other) {
410            // Same types use direct comparison
411            (DataValue::Null, DataValue::Null) => true,
412            (DataValue::Bool(a), DataValue::Bool(b)) => a == b,
413            (DataValue::Number(a), DataValue::Number(b)) => a == b,
414
415            // Fast path for string comparison - avoid unnecessary allocations
416            (DataValue::String(a), DataValue::String(b)) => {
417                // First check if the pointers are the same (interned strings)
418                if std::ptr::eq(*a as *const str, *b as *const str) {
419                    return true;
420                }
421
422                // Then check if the lengths are different (quick rejection)
423                if a.len() != b.len() {
424                    return false;
425                }
426
427                // Finally, compare the actual strings
428                a == b
429            }
430
431            // DateTime and Duration direct comparisons
432            (DataValue::DateTime(a), DataValue::DateTime(b)) => a == b,
433            (DataValue::Duration(a), DataValue::Duration(b)) => a == b,
434
435            // Different types with coercion
436            (DataValue::Null, DataValue::Bool(b)) => !b,
437            (DataValue::Bool(a), DataValue::Null) => !a,
438
439            (DataValue::Number(a), DataValue::String(_)) => {
440                match other.coerce_to_number() {
441                    Some(b_value) => {
442                        let b_num = b_value.as_f64();
443                        let a_num = a.as_f64();
444                        // Compare with small epsilon for floating point
445                        (a_num - b_num).abs() < f64::EPSILON
446                    }
447                    None => false, // If we can't convert string to number, they're not equal
448                }
449            }
450            (DataValue::String(_), DataValue::Number(b)) => {
451                match self.coerce_to_number() {
452                    Some(a_value) => {
453                        let a_num = a_value.as_f64();
454                        let b_num = b.as_f64();
455                        // Compare with small epsilon for floating point
456                        (a_num - b_num).abs() < f64::EPSILON
457                    }
458                    None => false, // If we can't convert string to number, they're not equal
459                }
460            }
461
462            // Arrays and objects are compared by reference
463            (DataValue::Array(a), DataValue::Array(b)) => {
464                if a.len() != b.len() {
465                    return false;
466                }
467                a.iter()
468                    .zip(b.iter())
469                    .all(|(a_item, b_item)| a_item.equals(b_item))
470            }
471            (DataValue::Object(a), DataValue::Object(b)) => {
472                // Quick length check first
473                if a.len() != b.len() {
474                    return false;
475                }
476
477                // Check all key-value pairs in both directions
478                for (a_key, a_val) in *a {
479                    let found_pair = b
480                        .iter()
481                        .any(|(b_key, b_val)| *a_key == *b_key && a_val.equals(b_val));
482                    if !found_pair {
483                        return false;
484                    }
485                }
486
487                // All key-value pairs matched
488                true
489            }
490
491            // Other combinations are not equal
492            _ => false,
493        }
494    }
495
496    /// Checks if this value strictly equals another value, without type coercion.
497    pub fn strict_equals(&self, other: &DataValue<'a>) -> bool {
498        match (self, other) {
499            (DataValue::Null, DataValue::Null) => true,
500            (DataValue::Bool(a), DataValue::Bool(b)) => a == b,
501            (DataValue::Number(a), DataValue::Number(b)) => a == b,
502            (DataValue::String(a), DataValue::String(b)) => a == b,
503            (DataValue::DateTime(a), DataValue::DateTime(b)) => a == b,
504            (DataValue::Duration(a), DataValue::Duration(b)) => a == b,
505            (DataValue::Array(a), DataValue::Array(b)) => {
506                if a.len() != b.len() {
507                    return false;
508                }
509                a.iter()
510                    .zip(b.iter())
511                    .all(|(a_item, b_item)| a_item.strict_equals(b_item))
512            }
513            (DataValue::Object(a), DataValue::Object(b)) => {
514                if a.len() != b.len() {
515                    return false;
516                }
517
518                // Check that all keys in a exist in b with strictly equal values
519                for (a_key, a_value) in *a {
520                    let mut found = false;
521                    for (b_key, b_value) in *b {
522                        if a_key == b_key {
523                            if !a_value.strict_equals(b_value) {
524                                return false;
525                            }
526                            found = true;
527                            break;
528                        }
529                    }
530                    if !found {
531                        return false;
532                    }
533                }
534                true
535            }
536            _ => false, // Different types are never strictly equal
537        }
538    }
539}
540
541impl PartialOrd for DataValue<'_> {
542    #[inline]
543    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
544        match (self, other) {
545            // Fast paths for common cases
546            (DataValue::Number(a), DataValue::Number(b)) => a.partial_cmp(b),
547            (DataValue::String(a), DataValue::String(b)) => {
548                // First check if the pointers are the same (interned strings)
549                if std::ptr::eq(*a as *const str, *b as *const str) {
550                    return Some(Ordering::Equal);
551                }
552
553                // Then do the standard comparison
554                a.partial_cmp(b)
555            }
556            (DataValue::Bool(a), DataValue::Bool(b)) => a.partial_cmp(b),
557            (DataValue::Null, DataValue::Null) => Some(Ordering::Equal),
558
559            // DateTime and Duration comparisons
560            (DataValue::DateTime(a), DataValue::DateTime(b)) => a.partial_cmp(b),
561            (DataValue::Duration(a), DataValue::Duration(b)) => a.partial_cmp(b),
562
563            (DataValue::Array(a), DataValue::Array(b)) => {
564                // Fast path for empty arrays
565                if a.is_empty() && b.is_empty() {
566                    return Some(Ordering::Equal);
567                }
568
569                // Fast path for different length arrays
570                if a.len() != b.len() {
571                    return a.len().partial_cmp(&b.len());
572                }
573
574                // Compare arrays lexicographically
575                for i in 0..a.len() {
576                    match a[i].partial_cmp(&b[i]) {
577                        Some(Ordering::Equal) => continue,
578                        other => return other,
579                    }
580                }
581                Some(Ordering::Equal)
582            }
583
584            // Mixed types: convert to common type for comparison
585            (DataValue::Number(a), DataValue::String(b)) => {
586                if let Ok(b_num) = b.parse::<f64>() {
587                    let a_f64 = match a {
588                        NumberValue::Integer(i) => *i as f64,
589                        NumberValue::Float(f) => *f,
590                    };
591
592                    if a_f64 > b_num {
593                        Some(Ordering::Greater)
594                    } else if a_f64 < b_num {
595                        Some(Ordering::Less)
596                    } else {
597                        Some(Ordering::Equal)
598                    }
599                } else {
600                    None
601                }
602            }
603            (DataValue::String(a), DataValue::Number(b)) => {
604                if let Ok(a_num) = a.parse::<f64>() {
605                    let b_f64 = match b {
606                        NumberValue::Integer(i) => *i as f64,
607                        NumberValue::Float(f) => *f,
608                    };
609
610                    if a_num > b_f64 {
611                        Some(Ordering::Greater)
612                    } else if a_num < b_f64 {
613                        Some(Ordering::Less)
614                    } else {
615                        Some(Ordering::Equal)
616                    }
617                } else {
618                    None
619                }
620            }
621
622            // Other combinations are not comparable
623            _ => None,
624        }
625    }
626}
627
628impl fmt::Display for DataValue<'_> {
629    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
630        match self {
631            DataValue::Null => write!(f, "null"),
632            DataValue::Bool(b) => write!(f, "{}", b),
633            DataValue::Number(n) => write!(f, "{}", n),
634            DataValue::String(s) => write!(f, "\"{}\"", s.replace('"', "\\\"")),
635            DataValue::Array(a) => {
636                write!(f, "[")?;
637                for (i, v) in a.iter().enumerate() {
638                    if i > 0 {
639                        write!(f, ", ")?;
640                    }
641                    write!(f, "{}", v)?;
642                }
643                write!(f, "]")
644            }
645            DataValue::Object(o) => {
646                write!(f, "{{")?;
647                for (i, (k, v)) in o.iter().enumerate() {
648                    if i > 0 {
649                        write!(f, ", ")?;
650                    }
651                    write!(f, "\"{}\": {}", k, v)?;
652                }
653                write!(f, "}}")
654            }
655            DataValue::DateTime(dt) => {
656                write!(f, "\"{}\"", dt.to_rfc3339())
657            }
658            DataValue::Duration(d) => {
659                let days = d.num_days();
660                let hours = d.num_hours() % 24;
661                let minutes = d.num_minutes() % 60;
662                let seconds = d.num_seconds() % 60;
663                write!(f, "{}d:{}h:{}m:{}s", days, hours, minutes, seconds)
664            }
665        }
666    }
667}
668
669#[cfg(test)]
670mod tests {
671    use super::*;
672    use crate::arena::DataArena;
673    use chrono::TimeZone;
674
675    #[test]
676    fn test_data_value_creation() {
677        let arena = DataArena::new();
678
679        let null = DataValue::null();
680        let boolean = DataValue::bool(true);
681        let integer = DataValue::integer(42);
682        let float = DataValue::float(3.14);
683        let string = DataValue::string(&arena, "hello");
684
685        assert!(null.is_null());
686        assert!(boolean.is_bool());
687        assert!(integer.is_number());
688        assert!(float.is_number());
689        assert!(string.is_string());
690
691        assert_eq!(boolean.as_bool(), Some(true));
692        assert_eq!(integer.as_i64(), Some(42));
693        assert_eq!(float.as_f64(), Some(3.14));
694        assert_eq!(string.as_str(), Some("hello"));
695    }
696
697    #[test]
698    fn test_datetime_and_duration() {
699        // Test datetime creation and access
700        let dt = Utc.with_ymd_and_hms(2022, 7, 6, 13, 20, 6).unwrap();
701        let dt_value = DataValue::datetime(dt);
702
703        assert!(dt_value.is_datetime());
704        assert_eq!(dt_value.as_datetime(), Some(&dt));
705
706        // Test duration creation and access
707        let duration =
708            Duration::days(1) + Duration::hours(2) + Duration::minutes(3) + Duration::seconds(4);
709        let duration_value = DataValue::duration(duration);
710
711        assert!(duration_value.is_duration());
712        assert_eq!(duration_value.as_duration(), Some(&duration));
713    }
714
715    #[test]
716    fn test_array_and_object() {
717        let arena = DataArena::new();
718
719        // Create array
720        let array = DataValue::array(
721            &arena,
722            &[
723                DataValue::integer(1),
724                DataValue::integer(2),
725                DataValue::integer(3),
726            ],
727        );
728
729        assert!(array.is_array());
730        assert_eq!(array.as_array().unwrap().len(), 3);
731        assert_eq!(array.get_index(1).unwrap().as_i64(), Some(2));
732
733        // Create object
734        let key1 = arena.intern_str("a");
735        let key2 = arena.intern_str("b");
736
737        let object = DataValue::object(
738            &arena,
739            &[(key1, DataValue::integer(1)), (key2, DataValue::integer(2))],
740        );
741
742        assert!(object.is_object());
743        assert_eq!(object.as_object().unwrap().len(), 2);
744        assert_eq!(object.get("a").unwrap().as_i64(), Some(1));
745    }
746
747    #[test]
748    fn test_coercion() {
749        let arena = DataArena::new();
750
751        // Boolean coercion
752        assert!(!DataValue::null().coerce_to_bool());
753        assert!(DataValue::bool(true).coerce_to_bool());
754        assert!(!DataValue::integer(0).coerce_to_bool());
755        assert!(DataValue::integer(1).coerce_to_bool());
756        assert!(!DataValue::string(&arena, "").coerce_to_bool());
757        assert!(DataValue::string(&arena, "hello").coerce_to_bool());
758
759        // Test datetime and duration boolean coercion
760        let dt = Utc.with_ymd_and_hms(2022, 7, 6, 13, 20, 6).unwrap();
761        assert!(DataValue::datetime(dt).coerce_to_bool());
762
763        let duration = Duration::seconds(0);
764        assert!(!DataValue::duration(duration).coerce_to_bool());
765
766        let non_zero_duration = Duration::seconds(10);
767        assert!(DataValue::duration(non_zero_duration).coerce_to_bool());
768
769        // Number coercion
770        assert_eq!(
771            DataValue::null().coerce_to_number(),
772            Some(NumberValue::Integer(0))
773        );
774        assert_eq!(
775            DataValue::bool(true).coerce_to_number(),
776            Some(NumberValue::Integer(1))
777        );
778        assert_eq!(
779            DataValue::string(&arena, "42").coerce_to_number(),
780            Some(NumberValue::Integer(42))
781        );
782        assert_eq!(
783            DataValue::string(&arena, "3.14").coerce_to_number(),
784            Some(NumberValue::Float(3.14))
785        );
786
787        // Test datetime and duration number coercion
788        let dt = Utc.with_ymd_and_hms(2022, 7, 6, 13, 20, 6).unwrap();
789        let dt_num = DataValue::datetime(dt).coerce_to_number();
790        assert!(dt_num.is_some());
791
792        let duration = Duration::seconds(93784); // 1d:2h:3m:4s
793        assert_eq!(
794            DataValue::duration(duration).coerce_to_number(),
795            Some(NumberValue::Integer(93784))
796        );
797
798        // String coercion
799        assert_eq!(
800            DataValue::null().coerce_to_string(&arena).as_str(),
801            Some("null")
802        );
803        assert_eq!(
804            DataValue::bool(true).coerce_to_string(&arena).as_str(),
805            Some("true")
806        );
807        assert_eq!(
808            DataValue::integer(42).coerce_to_string(&arena).as_str(),
809            Some("42")
810        );
811
812        // Test datetime and duration string coercion
813        let dt = Utc.with_ymd_and_hms(2022, 7, 6, 13, 20, 6).unwrap();
814        let dt_value = DataValue::datetime(dt).coerce_to_string(&arena);
815        let dt_str = dt_value.as_str();
816        assert!(dt_str.is_some());
817        assert!(dt_str.unwrap().contains("2022-07-06T13:20:06"));
818
819        let duration =
820            Duration::days(1) + Duration::hours(2) + Duration::minutes(3) + Duration::seconds(4);
821        let dur_value = DataValue::duration(duration).coerce_to_string(&arena);
822        let dur_str = dur_value.as_str();
823        assert_eq!(dur_str, Some("1d:2h:3m:4s"));
824    }
825
826    #[test]
827    fn test_comparison() {
828        let arena = DataArena::new();
829
830        // Same types
831        assert!(DataValue::null() == DataValue::null());
832        assert!(DataValue::bool(true) > DataValue::bool(false));
833        assert!(DataValue::integer(5) > DataValue::integer(3));
834        assert!(DataValue::float(3.14) > DataValue::float(2.71));
835        assert!(DataValue::string(&arena, "hello") == DataValue::string(&arena, "hello"));
836        assert!(DataValue::string(&arena, "world") > DataValue::string(&arena, "hello"));
837
838        // Test datetime comparison
839        let dt1 = Utc.with_ymd_and_hms(2022, 7, 6, 13, 20, 6).unwrap();
840        let dt2 = Utc.with_ymd_and_hms(2022, 7, 7, 13, 20, 6).unwrap();
841
842        assert!(DataValue::datetime(dt1) < DataValue::datetime(dt2));
843        assert!(DataValue::datetime(dt2) > DataValue::datetime(dt1));
844        assert!(DataValue::datetime(dt1) == DataValue::datetime(dt1));
845
846        // Test duration comparison
847        let dur1 = Duration::days(1);
848        let dur2 = Duration::days(2);
849
850        assert!(DataValue::duration(dur1) < DataValue::duration(dur2));
851        assert!(DataValue::duration(dur2) > DataValue::duration(dur1));
852        assert!(DataValue::duration(dur1) == DataValue::duration(dur1));
853
854        // Mixed types
855        assert!(DataValue::integer(42) == DataValue::float(42.0));
856
857        // Array comparison
858        let array1 = DataValue::array(&arena, &[DataValue::integer(1), DataValue::integer(2)]);
859        let array2 = DataValue::array(&arena, &[DataValue::integer(1), DataValue::integer(3)]);
860        assert!(array1 < array2);
861    }
862}