1use crate::api::error::UniError;
15use crate::core::id::{Eid, Vid};
16use serde::{Deserialize, Serialize};
17use std::collections::HashMap;
18use std::fmt;
19use std::hash::{Hash, Hasher};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27pub enum TemporalType {
28 Date,
29 LocalTime,
30 Time,
31 LocalDateTime,
32 DateTime,
33 Duration,
34 Btic,
35}
36
37#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
43pub enum TemporalValue {
44 Date { days_since_epoch: i32 },
46 LocalTime { nanos_since_midnight: i64 },
48 Time {
50 nanos_since_midnight: i64,
51 offset_seconds: i32,
52 },
53 LocalDateTime { nanos_since_epoch: i64 },
55 DateTime {
58 nanos_since_epoch: i64,
59 offset_seconds: i32,
60 timezone_name: Option<String>,
61 },
62 Duration { months: i64, days: i64, nanos: i64 },
65 Btic { lo: i64, hi: i64, meta: u64 },
68}
69
70impl Eq for TemporalValue {}
71
72impl Hash for TemporalValue {
73 fn hash<H: Hasher>(&self, state: &mut H) {
74 std::mem::discriminant(self).hash(state);
75 match self {
76 TemporalValue::Date { days_since_epoch } => days_since_epoch.hash(state),
77 TemporalValue::LocalTime {
78 nanos_since_midnight,
79 } => nanos_since_midnight.hash(state),
80 TemporalValue::Time {
81 nanos_since_midnight,
82 offset_seconds,
83 } => {
84 nanos_since_midnight.hash(state);
85 offset_seconds.hash(state);
86 }
87 TemporalValue::LocalDateTime { nanos_since_epoch } => nanos_since_epoch.hash(state),
88 TemporalValue::DateTime {
89 nanos_since_epoch,
90 offset_seconds,
91 timezone_name,
92 } => {
93 nanos_since_epoch.hash(state);
94 offset_seconds.hash(state);
95 timezone_name.hash(state);
96 }
97 TemporalValue::Duration {
98 months,
99 days,
100 nanos,
101 } => {
102 months.hash(state);
103 days.hash(state);
104 nanos.hash(state);
105 }
106 TemporalValue::Btic { lo, hi, meta } => {
107 lo.hash(state);
108 hi.hash(state);
109 meta.hash(state);
110 }
111 }
112 }
113}
114
115impl TemporalValue {
116 pub fn temporal_type(&self) -> TemporalType {
118 match self {
119 TemporalValue::Date { .. } => TemporalType::Date,
120 TemporalValue::LocalTime { .. } => TemporalType::LocalTime,
121 TemporalValue::Time { .. } => TemporalType::Time,
122 TemporalValue::LocalDateTime { .. } => TemporalType::LocalDateTime,
123 TemporalValue::DateTime { .. } => TemporalType::DateTime,
124 TemporalValue::Duration { .. } => TemporalType::Duration,
125 TemporalValue::Btic { .. } => TemporalType::Btic,
126 }
127 }
128
129 pub fn year(&self) -> Option<i64> {
135 self.to_date().map(|d| d.year() as i64)
136 }
137
138 pub fn month(&self) -> Option<i64> {
140 self.to_date().map(|d| d.month() as i64)
141 }
142
143 pub fn day(&self) -> Option<i64> {
145 self.to_date().map(|d| d.day() as i64)
146 }
147
148 pub fn hour(&self) -> Option<i64> {
150 self.to_time().map(|t| t.hour() as i64)
151 }
152
153 pub fn minute(&self) -> Option<i64> {
155 self.to_time().map(|t| t.minute() as i64)
156 }
157
158 pub fn second(&self) -> Option<i64> {
160 self.to_time().map(|t| t.second() as i64)
161 }
162
163 pub fn millisecond(&self) -> Option<i64> {
165 self.to_time().map(|t| (t.nanosecond() / 1_000_000) as i64)
166 }
167
168 pub fn microsecond(&self) -> Option<i64> {
170 self.to_time().map(|t| (t.nanosecond() / 1_000) as i64)
171 }
172
173 pub fn nanosecond(&self) -> Option<i64> {
175 self.to_time().map(|t| t.nanosecond() as i64)
176 }
177
178 pub fn quarter(&self) -> Option<i64> {
180 self.to_date().map(|d| ((d.month() - 1) / 3 + 1) as i64)
181 }
182
183 pub fn week(&self) -> Option<i64> {
185 self.to_date().map(|d| d.iso_week().week() as i64)
186 }
187
188 pub fn week_year(&self) -> Option<i64> {
190 self.to_date().map(|d| d.iso_week().year() as i64)
191 }
192
193 pub fn ordinal_day(&self) -> Option<i64> {
195 self.to_date().map(|d| d.ordinal() as i64)
196 }
197
198 pub fn day_of_week(&self) -> Option<i64> {
200 self.to_date()
201 .map(|d| (d.weekday().num_days_from_monday() + 1) as i64)
202 }
203
204 pub fn day_of_quarter(&self) -> Option<i64> {
206 self.to_date().map(|d| {
207 let quarter_start_month = ((d.month() - 1) / 3) * 3 + 1;
208 let quarter_start =
209 chrono::NaiveDate::from_ymd_opt(d.year(), quarter_start_month, 1).unwrap();
210 d.signed_duration_since(quarter_start).num_days() + 1
211 })
212 }
213
214 pub fn timezone(&self) -> Option<&str> {
216 match self {
217 TemporalValue::DateTime {
218 timezone_name: Some(name),
219 ..
220 } => Some(name.as_str()),
221 _ => None,
222 }
223 }
224
225 fn raw_offset_seconds(&self) -> Option<i32> {
227 match self {
228 TemporalValue::Time { offset_seconds, .. }
229 | TemporalValue::DateTime { offset_seconds, .. } => Some(*offset_seconds),
230 _ => None,
231 }
232 }
233
234 pub fn offset(&self) -> Option<String> {
236 self.raw_offset_seconds().map(format_offset)
237 }
238
239 pub fn offset_minutes(&self) -> Option<i64> {
241 self.raw_offset_seconds().map(|s| s as i64 / 60)
242 }
243
244 pub fn offset_seconds_value(&self) -> Option<i64> {
246 self.raw_offset_seconds().map(|s| s as i64)
247 }
248
249 fn raw_epoch_nanos(&self) -> Option<i64> {
251 match self {
252 TemporalValue::DateTime {
253 nanos_since_epoch, ..
254 }
255 | TemporalValue::LocalDateTime {
256 nanos_since_epoch, ..
257 } => Some(*nanos_since_epoch),
258 _ => None,
259 }
260 }
261
262 pub fn epoch_seconds(&self) -> Option<i64> {
264 self.raw_epoch_nanos().map(|n| n / 1_000_000_000)
265 }
266
267 pub fn epoch_millis(&self) -> Option<i64> {
269 self.raw_epoch_nanos().map(|n| n / 1_000_000)
270 }
271
272 pub fn to_date(&self) -> Option<chrono::NaiveDate> {
278 let epoch = chrono::NaiveDate::from_ymd_opt(1970, 1, 1)?;
279 match self {
280 TemporalValue::Date { days_since_epoch } => {
281 epoch.checked_add_signed(chrono::Duration::days(*days_since_epoch as i64))
282 }
283 TemporalValue::LocalDateTime { nanos_since_epoch } => {
284 let dt = chrono::DateTime::from_timestamp_nanos(*nanos_since_epoch);
285 Some(dt.date_naive())
286 }
287 TemporalValue::DateTime {
288 nanos_since_epoch,
289 offset_seconds,
290 ..
291 } => {
292 let local_nanos = nanos_since_epoch + (*offset_seconds as i64) * 1_000_000_000;
294 let dt = chrono::DateTime::from_timestamp_nanos(local_nanos);
295 Some(dt.date_naive())
296 }
297 _ => None,
298 }
299 }
300
301 pub fn to_time(&self) -> Option<chrono::NaiveTime> {
303 match self {
304 TemporalValue::LocalTime {
305 nanos_since_midnight,
306 }
307 | TemporalValue::Time {
308 nanos_since_midnight,
309 ..
310 } => nanos_to_time(*nanos_since_midnight),
311 TemporalValue::LocalDateTime { nanos_since_epoch } => {
312 let dt = chrono::DateTime::from_timestamp_nanos(*nanos_since_epoch);
313 Some(dt.naive_utc().time())
314 }
315 TemporalValue::DateTime {
316 nanos_since_epoch,
317 offset_seconds,
318 ..
319 } => {
320 let local_nanos = nanos_since_epoch + (*offset_seconds as i64) * 1_000_000_000;
321 let dt = chrono::DateTime::from_timestamp_nanos(local_nanos);
322 Some(dt.naive_utc().time())
323 }
324 _ => None,
325 }
326 }
327}
328
329fn nanos_to_time(nanos: i64) -> Option<chrono::NaiveTime> {
331 let total_secs = nanos / 1_000_000_000;
332 let h = (total_secs / 3600) as u32;
333 let m = ((total_secs % 3600) / 60) as u32;
334 let s = (total_secs % 60) as u32;
335 let ns = (nanos % 1_000_000_000) as u32;
336 chrono::NaiveTime::from_hms_nano_opt(h, m, s, ns)
337}
338
339fn format_offset(offset_seconds: i32) -> String {
341 if offset_seconds == 0 {
342 return "Z".to_string();
343 }
344 format_offset_numeric(offset_seconds)
345}
346
347fn format_offset_numeric(offset_seconds: i32) -> String {
349 let sign = if offset_seconds >= 0 { '+' } else { '-' };
350 let abs = offset_seconds.unsigned_abs();
351 let h = abs / 3600;
352 let m = (abs % 3600) / 60;
353 let s = abs % 60;
354 if s != 0 {
355 format!("{}{:02}:{:02}:{:02}", sign, h, m, s)
356 } else {
357 format!("{}{:02}:{:02}", sign, h, m)
358 }
359}
360
361fn format_fractional(nanos: u32) -> String {
363 if nanos == 0 {
364 return String::new();
365 }
366 let s = format!("{:09}", nanos);
367 let trimmed = s.trim_end_matches('0');
368 format!(".{}", trimmed)
369}
370
371fn format_time_component(hour: u32, minute: u32, second: u32, nanos: u32) -> String {
373 if second == 0 && nanos == 0 {
374 format!("{:02}:{:02}", hour, minute)
375 } else {
376 let frac = format_fractional(nanos);
377 format!("{:02}:{:02}:{:02}{}", hour, minute, second, frac)
378 }
379}
380
381fn format_naive_time(t: &chrono::NaiveTime) -> String {
383 format_time_component(t.hour(), t.minute(), t.second(), t.nanosecond())
384}
385
386fn nanos_to_time_or_midnight(nanos: i64) -> chrono::NaiveTime {
388 nanos_to_time(nanos).unwrap_or_else(|| chrono::NaiveTime::from_hms_opt(0, 0, 0).unwrap())
389}
390
391impl fmt::Display for TemporalValue {
392 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393 match self {
394 TemporalValue::Date { days_since_epoch } => {
395 let epoch = chrono::NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
396 let date = epoch
401 .checked_add_signed(chrono::Duration::days(*days_since_epoch as i64))
402 .unwrap_or(if *days_since_epoch >= 0 {
403 chrono::NaiveDate::MAX
404 } else {
405 chrono::NaiveDate::MIN
406 });
407 write!(f, "{}", date.format("%Y-%m-%d"))
408 }
409 TemporalValue::LocalTime {
410 nanos_since_midnight,
411 } => {
412 let time = nanos_to_time_or_midnight(*nanos_since_midnight);
413 write!(f, "{}", format_naive_time(&time))
414 }
415 TemporalValue::Time {
416 nanos_since_midnight,
417 offset_seconds,
418 } => {
419 let time = nanos_to_time_or_midnight(*nanos_since_midnight);
420 write!(
421 f,
422 "{}{}",
423 format_naive_time(&time),
424 format_offset(*offset_seconds)
425 )
426 }
427 TemporalValue::LocalDateTime { nanos_since_epoch } => {
428 let ndt = chrono::DateTime::from_timestamp_nanos(*nanos_since_epoch).naive_utc();
429 write!(
430 f,
431 "{}T{}",
432 ndt.date().format("%Y-%m-%d"),
433 format_naive_time(&ndt.time())
434 )
435 }
436 TemporalValue::DateTime {
437 nanos_since_epoch,
438 offset_seconds,
439 timezone_name,
440 } => {
441 let local_nanos = nanos_since_epoch + (*offset_seconds as i64) * 1_000_000_000;
443 let ndt = chrono::DateTime::from_timestamp_nanos(local_nanos).naive_utc();
444 let tz = format_offset(*offset_seconds);
445 write!(
446 f,
447 "{}T{}{}",
448 ndt.date().format("%Y-%m-%d"),
449 format_naive_time(&ndt.time()),
450 tz
451 )?;
452 if let Some(name) = timezone_name {
453 write!(f, "[{}]", name)?;
454 }
455 Ok(())
456 }
457 TemporalValue::Duration {
458 months,
459 days,
460 nanos,
461 } => {
462 write!(f, "P")?;
463 let years = months / 12;
464 let rem_months = months % 12;
465 if years != 0 {
466 write!(f, "{}Y", years)?;
467 }
468 if rem_months != 0 {
469 write!(f, "{}M", rem_months)?;
470 }
471 if *days != 0 {
472 write!(f, "{}D", days)?;
473 }
474 let abs_nanos = nanos.unsigned_abs() as i128;
476 let nanos_sign = if *nanos < 0 { -1i64 } else { 1 };
477 let total_secs = (abs_nanos / 1_000_000_000) as i64;
478 let frac_nanos = (abs_nanos % 1_000_000_000) as u32;
479 let hours = total_secs / 3600;
480 let mins = (total_secs % 3600) / 60;
481 let secs = total_secs % 60;
482
483 if hours != 0 || mins != 0 || secs != 0 || frac_nanos != 0 {
484 write!(f, "T")?;
485 if hours != 0 {
486 write!(f, "{}H", hours * nanos_sign)?;
487 }
488 if mins != 0 {
489 write!(f, "{}M", mins * nanos_sign)?;
490 }
491 if secs != 0 || frac_nanos != 0 {
492 let frac = format_fractional(frac_nanos);
493 if nanos_sign < 0 && (secs != 0 || frac_nanos != 0) {
494 write!(f, "-{}{}", secs, frac)?;
495 } else {
496 write!(f, "{}{}", secs, frac)?;
497 }
498 write!(f, "S")?;
499 }
500 } else if years == 0 && rem_months == 0 && *days == 0 {
501 write!(f, "T0S")?;
503 }
504 Ok(())
505 }
506 TemporalValue::Btic { lo, hi, meta } => match uni_btic::Btic::new(*lo, *hi, *meta) {
507 Ok(btic) => write!(f, "{btic}"),
508 Err(_) => write!(f, "Btic[lo={lo}, hi={hi}, meta={meta:#x}]"),
509 },
510 }
511 }
512}
513
514use chrono::Datelike as _;
516use chrono::Timelike as _;
517
518#[derive(Debug, Clone, Serialize, Deserialize)]
531#[serde(untagged)]
532#[non_exhaustive]
533pub enum Value {
534 Null,
536 Bool(bool),
538 Int(i64),
540 Float(f64),
542 String(String),
544 Bytes(Vec<u8>),
546 List(Vec<Value>),
548 Map(HashMap<String, Value>),
550
551 Node(Node),
554 Edge(Edge),
556 Path(Path),
558
559 Vector(Vec<f32>),
562
563 SparseVector {
570 indices: Vec<u32>,
572 values: Vec<f32>,
574 },
575
576 BinaryVector(Vec<u8>),
581
582 Temporal(TemporalValue),
585}
586
587impl Value {
592 pub fn is_null(&self) -> bool {
594 matches!(self, Value::Null)
595 }
596
597 pub fn as_bool(&self) -> Option<bool> {
599 match self {
600 Value::Bool(b) => Some(*b),
601 _ => None,
602 }
603 }
604
605 pub fn as_i64(&self) -> Option<i64> {
607 match self {
608 Value::Int(i) => Some(*i),
609 _ => None,
610 }
611 }
612
613 pub fn as_u64(&self) -> Option<u64> {
615 match self {
616 Value::Int(i) if *i >= 0 => Some(*i as u64),
617 _ => None,
618 }
619 }
620
621 pub fn as_f64(&self) -> Option<f64> {
625 match self {
626 Value::Float(f) => Some(*f),
627 Value::Int(i) => Some(*i as f64),
628 _ => None,
629 }
630 }
631
632 pub fn as_str(&self) -> Option<&str> {
634 match self {
635 Value::String(s) => Some(s),
636 _ => None,
637 }
638 }
639
640 pub fn is_i64(&self) -> bool {
642 matches!(self, Value::Int(_))
643 }
644
645 pub fn is_f64(&self) -> bool {
647 matches!(self, Value::Float(_))
648 }
649
650 pub fn is_string(&self) -> bool {
652 matches!(self, Value::String(_))
653 }
654
655 pub fn is_number(&self) -> bool {
657 matches!(self, Value::Int(_) | Value::Float(_))
658 }
659
660 pub fn as_array(&self) -> Option<&Vec<Value>> {
662 match self {
663 Value::List(l) => Some(l),
664 _ => None,
665 }
666 }
667
668 pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
670 match self {
671 Value::Map(m) => Some(m),
672 _ => None,
673 }
674 }
675
676 pub fn is_bool(&self) -> bool {
678 matches!(self, Value::Bool(_))
679 }
680
681 pub fn is_list(&self) -> bool {
683 matches!(self, Value::List(_))
684 }
685
686 pub fn is_map(&self) -> bool {
688 matches!(self, Value::Map(_))
689 }
690
691 pub fn get(&self, key: &str) -> Option<&Value> {
695 match self {
696 Value::Map(m) => m.get(key),
697 _ => None,
698 }
699 }
700
701 pub fn is_temporal(&self) -> bool {
703 matches!(self, Value::Temporal(_))
704 }
705
706 pub fn as_temporal(&self) -> Option<&TemporalValue> {
708 match self {
709 Value::Temporal(t) => Some(t),
710 _ => None,
711 }
712 }
713}
714
715impl fmt::Display for Value {
716 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
717 match self {
718 Value::Null => write!(f, "null"),
719 Value::Bool(b) => write!(f, "{b}"),
720 Value::Int(i) => write!(f, "{i}"),
721 Value::Float(v) => {
722 if v.fract() == 0.0 && v.is_finite() {
723 write!(f, "{v:.1}")
724 } else {
725 write!(f, "{v}")
726 }
727 }
728 Value::String(s) => write!(f, "{s}"),
729 Value::Bytes(b) => write!(f, "<{} bytes>", b.len()),
730 Value::List(l) => {
731 write!(f, "[")?;
732 for (i, item) in l.iter().enumerate() {
733 if i > 0 {
734 write!(f, ", ")?;
735 }
736 write!(f, "{item}")?;
737 }
738 write!(f, "]")
739 }
740 Value::Map(m) => {
741 write!(f, "{{")?;
742 for (i, (k, v)) in m.iter().enumerate() {
743 if i > 0 {
744 write!(f, ", ")?;
745 }
746 write!(f, "{k}: {v}")?;
747 }
748 write!(f, "}}")
749 }
750 Value::Node(n) => write!(f, "(:{} {{vid: {}}})", n.labels.join(":"), n.vid),
751 Value::Edge(e) => write!(f, "-[:{}]-", e.edge_type),
752 Value::Path(p) => write!(
753 f,
754 "<path: {} nodes, {} edges>",
755 p.nodes.len(),
756 p.edges.len()
757 ),
758 Value::Vector(v) => write!(f, "<vector: {} dims>", v.len()),
759 Value::SparseVector { indices, .. } => {
760 write!(f, "<sparse vector: {} nnz>", indices.len())
761 }
762 Value::BinaryVector(bytes) => write!(f, "<binary vector: {} lanes>", bytes.len()),
763 Value::Temporal(t) => write!(f, "{t}"),
764 }
765 }
766}
767
768pub fn cmp_i64_f64(i: i64, f: f64) -> std::cmp::Ordering {
791 use std::cmp::Ordering;
792 if f.is_infinite() {
793 return if f > 0.0 {
794 Ordering::Less
795 } else {
796 Ordering::Greater
797 };
798 }
799 let ff = f.floor();
800 if ff >= 9_223_372_036_854_775_808.0 {
802 return Ordering::Less;
803 }
804 if ff < -9_223_372_036_854_775_808.0 {
806 return Ordering::Greater;
807 }
808 let fi = ff as i64;
810 match i.cmp(&fi) {
811 Ordering::Equal if f > ff => Ordering::Less,
813 Ordering::Equal => Ordering::Equal,
814 other => other,
815 }
816}
817
818fn float_eq_normalized(a: f64, b: f64) -> bool {
825 a.total_cmp(&b) == std::cmp::Ordering::Equal
826 || (a == 0.0 && b == 0.0)
827 || (a.is_nan() && b.is_nan())
828}
829
830fn float_eq_normalized_f32(a: f32, b: f32) -> bool {
839 a.total_cmp(&b) == std::cmp::Ordering::Equal
840 || (a == 0.0 && b == 0.0)
841 || (a.is_nan() && b.is_nan())
842}
843
844fn slice_eq_normalized_f32(a: &[f32], b: &[f32]) -> bool {
849 a.len() == b.len()
850 && a.iter()
851 .zip(b)
852 .all(|(x, y)| float_eq_normalized_f32(*x, *y))
853}
854
855impl PartialEq for Value {
856 fn eq(&self, other: &Self) -> bool {
863 match (self, other) {
864 (Value::Float(a), Value::Float(b)) => float_eq_normalized(*a, *b),
866 (Value::Null, Value::Null) => true,
868 (Value::Bool(a), Value::Bool(b)) => a == b,
869 (Value::Int(a), Value::Int(b)) => a == b,
870 (Value::String(a), Value::String(b)) => a == b,
871 (Value::Bytes(a), Value::Bytes(b)) => a == b,
872 (Value::List(a), Value::List(b)) => a == b,
873 (Value::Map(a), Value::Map(b)) => a == b,
874 (Value::Node(a), Value::Node(b)) => a == b,
875 (Value::Edge(a), Value::Edge(b)) => a == b,
876 (Value::Path(a), Value::Path(b)) => a == b,
877 (Value::Vector(a), Value::Vector(b)) => slice_eq_normalized_f32(a, b),
881 (
882 Value::SparseVector {
883 indices: i1,
884 values: v1,
885 },
886 Value::SparseVector {
887 indices: i2,
888 values: v2,
889 },
890 ) => i1 == i2 && slice_eq_normalized_f32(v1, v2),
891 (Value::BinaryVector(a), Value::BinaryVector(b)) => a == b,
894 (Value::Temporal(a), Value::Temporal(b)) => a == b,
895 _ => false,
897 }
898 }
899}
900
901impl Eq for Value {}
902
903fn hash_f64_normalized<H: Hasher>(f: f64, state: &mut H) {
908 let bits = if f == 0.0 {
909 0.0f64.to_bits()
910 } else if f.is_nan() {
911 f64::NAN.to_bits()
912 } else {
913 f.to_bits()
914 };
915 bits.hash(state);
916}
917
918fn hash_f32_normalized<H: Hasher>(f: f32, state: &mut H) {
925 let bits = if f == 0.0 {
926 0.0f32.to_bits()
927 } else if f.is_nan() {
928 f32::NAN.to_bits()
929 } else {
930 f.to_bits()
931 };
932 bits.hash(state);
933}
934
935impl Hash for Value {
936 fn hash<H: Hasher>(&self, state: &mut H) {
937 std::mem::discriminant(self).hash(state);
939 match self {
940 Value::Null => {}
941 Value::Bool(b) => b.hash(state),
942 Value::Int(i) => i.hash(state),
943 Value::Float(f) => hash_f64_normalized(*f, state),
947 Value::String(s) => s.hash(state),
948 Value::Bytes(b) => b.hash(state),
949 Value::List(l) => l.hash(state),
950 Value::Map(m) => hash_map(m, state),
951 Value::Node(n) => n.hash(state),
952 Value::Edge(e) => e.hash(state),
953 Value::Path(p) => p.hash(state),
954 Value::Vector(v) => {
955 v.len().hash(state);
958 for f in v {
959 hash_f32_normalized(*f, state);
960 }
961 }
962 Value::SparseVector { indices, values } => {
963 indices.hash(state);
967 values.len().hash(state);
968 for f in values {
969 hash_f32_normalized(*f, state);
970 }
971 }
972 Value::BinaryVector(b) => b.hash(state),
975 Value::Temporal(t) => t.hash(state),
976 }
977 }
978}
979
980fn hash_map<H: Hasher>(m: &HashMap<String, Value>, state: &mut H) {
986 let mut pairs: Vec<_> = m.iter().collect();
987 pairs.sort_by_key(|(k, _)| *k);
988 pairs.len().hash(state);
989 for (k, v) in pairs {
990 k.hash(state);
991 v.hash(state);
992 }
993}
994
995#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
997pub struct Node {
998 pub vid: Vid,
1000 pub labels: Vec<String>,
1002 pub properties: HashMap<String, Value>,
1004}
1005
1006impl Hash for Node {
1007 fn hash<H: Hasher>(&self, state: &mut H) {
1008 self.vid.hash(state);
1009 let mut sorted_labels = self.labels.clone();
1010 sorted_labels.sort();
1011 sorted_labels.hash(state);
1012 hash_map(&self.properties, state);
1013 }
1014}
1015
1016impl Node {
1017 pub fn get<T: FromValue>(&self, property: &str) -> crate::Result<T> {
1024 let val = self
1025 .properties
1026 .get(property)
1027 .ok_or_else(|| UniError::Query {
1028 message: format!("Property '{}' not found on node {}", property, self.vid),
1029 query: None,
1030 })?;
1031 T::from_value(val)
1032 }
1033
1034 pub fn try_get<T: FromValue>(&self, property: &str) -> Option<T> {
1036 self.properties
1037 .get(property)
1038 .and_then(|v| T::from_value(v).ok())
1039 }
1040}
1041
1042#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1044pub struct Edge {
1045 pub eid: Eid,
1047 pub edge_type: String,
1049 pub src: Vid,
1051 pub dst: Vid,
1053 pub properties: HashMap<String, Value>,
1055}
1056
1057impl Hash for Edge {
1058 fn hash<H: Hasher>(&self, state: &mut H) {
1059 self.eid.hash(state);
1060 self.edge_type.hash(state);
1061 self.src.hash(state);
1062 self.dst.hash(state);
1063 hash_map(&self.properties, state);
1064 }
1065}
1066
1067impl Edge {
1068 pub fn get<T: FromValue>(&self, property: &str) -> crate::Result<T> {
1075 let val = self
1076 .properties
1077 .get(property)
1078 .ok_or_else(|| UniError::Query {
1079 message: format!("Property '{}' not found on edge {}", property, self.eid),
1080 query: None,
1081 })?;
1082 T::from_value(val)
1083 }
1084}
1085
1086#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
1088pub struct Path {
1089 pub nodes: Vec<Node>,
1091 #[serde(rename = "relationships")]
1093 pub edges: Vec<Edge>,
1094}
1095
1096impl Path {
1097 pub fn nodes(&self) -> &[Node] {
1099 &self.nodes
1100 }
1101
1102 pub fn edges(&self) -> &[Edge] {
1104 &self.edges
1105 }
1106
1107 pub fn len(&self) -> usize {
1109 self.edges.len()
1110 }
1111
1112 pub fn is_empty(&self) -> bool {
1114 self.edges.is_empty()
1115 }
1116
1117 pub fn start(&self) -> Option<&Node> {
1119 self.nodes.first()
1120 }
1121
1122 pub fn end(&self) -> Option<&Node> {
1124 self.nodes.last()
1125 }
1126}
1127
1128pub trait FromValue: Sized {
1134 fn from_value(value: &Value) -> crate::Result<Self>;
1140}
1141
1142impl<T> FromValue for T
1144where
1145 T: for<'a> TryFrom<&'a Value, Error = UniError>,
1146{
1147 fn from_value(value: &Value) -> crate::Result<Self> {
1148 Self::try_from(value)
1149 }
1150}
1151
1152macro_rules! impl_try_from_value_owned {
1157 ($($t:ty),+ $(,)?) => {
1158 $(
1159 impl TryFrom<Value> for $t {
1160 type Error = UniError;
1161 fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
1162 Self::try_from(&value)
1163 }
1164 }
1165 )+
1166 };
1167}
1168
1169impl_try_from_value_owned!(
1170 String,
1171 i64,
1172 i32,
1173 f64,
1174 bool,
1175 Vid,
1176 Eid,
1177 Vec<f32>,
1178 Path,
1179 Node,
1180 Edge
1181);
1182
1183fn type_error(expected: &str, value: &Value) -> UniError {
1189 UniError::Type {
1190 expected: expected.to_string(),
1191 actual: format!("{:?}", value),
1192 }
1193}
1194
1195impl TryFrom<&Value> for String {
1196 type Error = UniError;
1197
1198 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1199 match value {
1200 Value::String(s) => Ok(s.clone()),
1201 Value::Int(i) => Ok(i.to_string()),
1202 Value::Float(f) => Ok(f.to_string()),
1203 Value::Bool(b) => Ok(b.to_string()),
1204 Value::Temporal(t) => Ok(t.to_string()),
1205 _ => Err(type_error("String", value)),
1206 }
1207 }
1208}
1209
1210impl TryFrom<&Value> for i64 {
1211 type Error = UniError;
1212
1213 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1220 match value {
1221 Value::Int(i) => Ok(*i),
1222 Value::Float(f) => Ok(*f as i64),
1223 _ => Err(type_error("Int", value)),
1224 }
1225 }
1226}
1227
1228impl TryFrom<&Value> for i32 {
1229 type Error = UniError;
1230
1231 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1236 match value {
1237 Value::Int(i) => i32::try_from(*i).map_err(|_| UniError::Type {
1238 expected: "i32".to_string(),
1239 actual: format!("Integer {} out of range", i),
1240 }),
1241 Value::Float(f) => {
1242 if *f < i32::MIN as f64 || *f > i32::MAX as f64 {
1243 return Err(UniError::Type {
1244 expected: "i32".to_string(),
1245 actual: format!("Float {} out of range", f),
1246 });
1247 }
1248 if f.fract() != 0.0 {
1249 return Err(UniError::Type {
1250 expected: "i32".to_string(),
1251 actual: format!("Float {} has fractional part", f),
1252 });
1253 }
1254 Ok(*f as i32)
1255 }
1256 _ => Err(type_error("Int", value)),
1257 }
1258 }
1259}
1260
1261impl TryFrom<&Value> for f64 {
1262 type Error = UniError;
1263
1264 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1265 match value {
1266 Value::Float(f) => Ok(*f),
1267 Value::Int(i) => Ok(*i as f64),
1268 _ => Err(type_error("Float", value)),
1269 }
1270 }
1271}
1272
1273impl TryFrom<&Value> for bool {
1274 type Error = UniError;
1275
1276 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1277 match value {
1278 Value::Bool(b) => Ok(*b),
1279 _ => Err(type_error("Bool", value)),
1280 }
1281 }
1282}
1283
1284impl TryFrom<&Value> for Vid {
1285 type Error = UniError;
1286
1287 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1288 match value {
1289 Value::Node(n) => Ok(n.vid),
1290 Value::String(s) => {
1291 if let Ok(id) = s.parse::<u64>() {
1292 return Ok(Vid::new(id));
1293 }
1294 Err(UniError::Type {
1295 expected: "Vid".into(),
1296 actual: s.clone(),
1297 })
1298 }
1299 Value::Int(i) => Ok(Vid::new(*i as u64)),
1300 _ => Err(type_error("Vid", value)),
1301 }
1302 }
1303}
1304
1305impl TryFrom<&Value> for Eid {
1306 type Error = UniError;
1307
1308 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1309 match value {
1310 Value::Edge(e) => Ok(e.eid),
1311 Value::String(s) => {
1312 if let Ok(id) = s.parse::<u64>() {
1313 return Ok(Eid::new(id));
1314 }
1315 Err(UniError::Type {
1316 expected: "Eid".into(),
1317 actual: s.clone(),
1318 })
1319 }
1320 Value::Int(i) => Ok(Eid::new(*i as u64)),
1321 _ => Err(type_error("Eid", value)),
1322 }
1323 }
1324}
1325
1326impl TryFrom<&Value> for Vec<f32> {
1327 type Error = UniError;
1328
1329 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1330 match value {
1331 Value::Vector(v) => Ok(v.clone()),
1332 Value::List(l) => {
1333 let mut vec = Vec::with_capacity(l.len());
1334 for item in l {
1335 match item {
1336 Value::Float(f) => vec.push(*f as f32),
1337 Value::Int(i) => vec.push(*i as f32),
1338 _ => return Err(type_error("Float", item)),
1339 }
1340 }
1341 Ok(vec)
1342 }
1343 _ => Err(type_error("Vector", value)),
1344 }
1345 }
1346}
1347
1348impl<T> TryFrom<&Value> for Option<T>
1349where
1350 T: for<'a> TryFrom<&'a Value, Error = UniError>,
1351{
1352 type Error = UniError;
1353
1354 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1355 match value {
1356 Value::Null => Ok(None),
1357 _ => T::try_from(value).map(Some),
1358 }
1359 }
1360}
1361
1362impl<T> TryFrom<Value> for Option<T>
1363where
1364 T: TryFrom<Value, Error = UniError>,
1365{
1366 type Error = UniError;
1367 fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
1368 match value {
1369 Value::Null => Ok(None),
1370 _ => T::try_from(value).map(Some),
1371 }
1372 }
1373}
1374
1375impl<T> TryFrom<&Value> for Vec<T>
1376where
1377 T: for<'a> TryFrom<&'a Value, Error = UniError>,
1378{
1379 type Error = UniError;
1380
1381 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1382 match value {
1383 Value::List(l) => {
1384 let mut vec = Vec::with_capacity(l.len());
1385 for item in l {
1386 vec.push(T::try_from(item)?);
1387 }
1388 Ok(vec)
1389 }
1390 _ => Err(type_error("List", value)),
1391 }
1392 }
1393}
1394
1395impl<T> TryFrom<Value> for Vec<T>
1396where
1397 T: TryFrom<Value, Error = UniError>,
1398{
1399 type Error = UniError;
1400 fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
1401 match value {
1402 Value::List(l) => {
1403 let mut vec = Vec::with_capacity(l.len());
1404 for item in l {
1405 vec.push(T::try_from(item)?);
1406 }
1407 Ok(vec)
1408 }
1409 other => Err(type_error("List", &other)),
1410 }
1411 }
1412}
1413
1414fn get_with_fallback<'a>(map: &'a HashMap<String, Value>, keys: &[&str]) -> Option<&'a Value> {
1420 keys.iter().find_map(|k| map.get(*k))
1421}
1422
1423fn extract_properties(value: &Value) -> HashMap<String, Value> {
1425 match value {
1426 Value::Map(m) => m.clone(),
1427 _ => HashMap::new(),
1428 }
1429}
1430
1431impl TryFrom<&Value> for Node {
1432 type Error = UniError;
1433
1434 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1435 match value {
1436 Value::Node(n) => Ok(n.clone()),
1437 Value::Map(m) => {
1438 let vid_val = get_with_fallback(m, &["_vid", "_id", "vid"]);
1439 let props_val = m.get("properties");
1440
1441 let (Some(v), Some(p)) = (vid_val, props_val) else {
1442 return Err(type_error("Node Map", value));
1443 };
1444
1445 let labels = if let Some(Value::List(label_list)) = m.get("_labels") {
1447 label_list
1448 .iter()
1449 .filter_map(|v| {
1450 if let Value::String(s) = v {
1451 Some(s.clone())
1452 } else {
1453 None
1454 }
1455 })
1456 .collect()
1457 } else {
1458 Vec::new()
1459 };
1460
1461 Ok(Node {
1462 vid: Vid::try_from(v)?,
1463 labels,
1464 properties: extract_properties(p),
1465 })
1466 }
1467 _ => Err(type_error("Node", value)),
1468 }
1469 }
1470}
1471
1472impl TryFrom<&Value> for Edge {
1473 type Error = UniError;
1474
1475 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1476 match value {
1477 Value::Edge(e) => Ok(e.clone()),
1478 Value::Map(m) => {
1479 let eid_val = get_with_fallback(m, &["_eid", "_id", "eid"]);
1480 let type_val = get_with_fallback(m, &["_type_name", "_type", "edge_type"]);
1481 let src_val = get_with_fallback(m, &["_src", "src"]);
1482 let dst_val = get_with_fallback(m, &["_dst", "dst"]);
1483 let props_val = m.get("properties");
1484
1485 let (Some(id), Some(t), Some(s), Some(d), Some(p)) =
1486 (eid_val, type_val, src_val, dst_val, props_val)
1487 else {
1488 return Err(type_error("Edge Map", value));
1489 };
1490
1491 Ok(Edge {
1492 eid: Eid::try_from(id)?,
1493 edge_type: String::try_from(t)?,
1494 src: Vid::try_from(s)?,
1495 dst: Vid::try_from(d)?,
1496 properties: extract_properties(p),
1497 })
1498 }
1499 _ => Err(type_error("Edge", value)),
1500 }
1501 }
1502}
1503
1504impl TryFrom<&Value> for Path {
1505 type Error = UniError;
1506
1507 fn try_from(value: &Value) -> std::result::Result<Self, Self::Error> {
1508 match value {
1509 Value::Path(p) => Ok(p.clone()),
1510 Value::Map(m) => {
1511 let (Some(Value::List(nodes_list)), Some(Value::List(rels_list))) =
1512 (m.get("nodes"), m.get("relationships"))
1513 else {
1514 return Err(type_error("Path (Map with nodes/relationships)", value));
1515 };
1516
1517 let nodes = nodes_list
1518 .iter()
1519 .map(Node::try_from)
1520 .collect::<std::result::Result<Vec<_>, _>>()?;
1521
1522 let edges = rels_list
1523 .iter()
1524 .map(Edge::try_from)
1525 .collect::<std::result::Result<Vec<_>, _>>()?;
1526
1527 Ok(Path { nodes, edges })
1528 }
1529 _ => Err(type_error("Path", value)),
1530 }
1531 }
1532}
1533
1534impl From<String> for Value {
1539 fn from(v: String) -> Self {
1540 Value::String(v)
1541 }
1542}
1543
1544impl From<&str> for Value {
1545 fn from(v: &str) -> Self {
1546 Value::String(v.to_string())
1547 }
1548}
1549
1550impl From<i64> for Value {
1551 fn from(v: i64) -> Self {
1552 Value::Int(v)
1553 }
1554}
1555
1556impl From<i32> for Value {
1557 fn from(v: i32) -> Self {
1558 Value::Int(v as i64)
1559 }
1560}
1561
1562impl From<f64> for Value {
1563 fn from(v: f64) -> Self {
1564 Value::Float(v)
1565 }
1566}
1567
1568impl From<bool> for Value {
1569 fn from(v: bool) -> Self {
1570 Value::Bool(v)
1571 }
1572}
1573
1574impl From<Vec<f32>> for Value {
1575 fn from(v: Vec<f32>) -> Self {
1576 Value::Vector(v)
1577 }
1578}
1579
1580impl From<serde_json::Value> for Value {
1585 fn from(v: serde_json::Value) -> Self {
1586 match v {
1587 serde_json::Value::Null => Value::Null,
1588 serde_json::Value::Bool(b) => Value::Bool(b),
1589 serde_json::Value::Number(n) => {
1590 if let Some(i) = n.as_i64() {
1591 Value::Int(i)
1592 } else if let Some(f) = n.as_f64() {
1593 Value::Float(f)
1594 } else {
1595 Value::Null
1596 }
1597 }
1598 serde_json::Value::String(s) => Value::String(s),
1599 serde_json::Value::Array(arr) => {
1600 Value::List(arr.into_iter().map(Value::from).collect())
1601 }
1602 serde_json::Value::Object(obj) => {
1603 Value::Map(obj.into_iter().map(|(k, v)| (k, Value::from(v))).collect())
1604 }
1605 }
1606 }
1607}
1608
1609impl From<Value> for serde_json::Value {
1610 fn from(v: Value) -> Self {
1611 match v {
1612 Value::Null => serde_json::Value::Null,
1613 Value::Bool(b) => serde_json::Value::Bool(b),
1614 Value::Int(i) => serde_json::Value::Number(serde_json::Number::from(i)),
1615 Value::Float(f) => serde_json::Number::from_f64(f)
1616 .map(serde_json::Value::Number)
1617 .unwrap_or(serde_json::Value::Null), Value::String(s) => serde_json::Value::String(s),
1619 Value::Bytes(b) => {
1620 use base64::Engine;
1621 serde_json::Value::String(base64::engine::general_purpose::STANDARD.encode(b))
1622 }
1623 Value::List(l) => {
1624 serde_json::Value::Array(l.into_iter().map(serde_json::Value::from).collect())
1625 }
1626 Value::Map(m) => {
1627 let mut map = serde_json::Map::new();
1628 for (k, v) in m {
1629 map.insert(k, v.into());
1630 }
1631 serde_json::Value::Object(map)
1632 }
1633 Value::Node(n) => {
1634 let mut map = serde_json::Map::new();
1635 map.insert(
1636 "_id".to_string(),
1637 serde_json::Value::String(n.vid.to_string()),
1638 );
1639 map.insert(
1640 "_labels".to_string(),
1641 serde_json::Value::Array(
1642 n.labels
1643 .into_iter()
1644 .map(serde_json::Value::String)
1645 .collect(),
1646 ),
1647 );
1648 let props: serde_json::Value = Value::Map(n.properties).into();
1649 map.insert("properties".to_string(), props);
1650 serde_json::Value::Object(map)
1651 }
1652 Value::Edge(e) => {
1653 let mut map = serde_json::Map::new();
1654 map.insert(
1655 "_id".to_string(),
1656 serde_json::Value::String(e.eid.to_string()),
1657 );
1658 map.insert("_type".to_string(), serde_json::Value::String(e.edge_type));
1659 map.insert(
1660 "_src".to_string(),
1661 serde_json::Value::String(e.src.to_string()),
1662 );
1663 map.insert(
1664 "_dst".to_string(),
1665 serde_json::Value::String(e.dst.to_string()),
1666 );
1667 let props: serde_json::Value = Value::Map(e.properties).into();
1668 map.insert("properties".to_string(), props);
1669 serde_json::Value::Object(map)
1670 }
1671 Value::Path(p) => {
1672 let mut map = serde_json::Map::new();
1673 map.insert(
1674 "nodes".to_string(),
1675 Value::List(p.nodes.into_iter().map(Value::Node).collect()).into(),
1676 );
1677 map.insert(
1678 "relationships".to_string(),
1679 Value::List(p.edges.into_iter().map(Value::Edge).collect()).into(),
1680 );
1681 serde_json::Value::Object(map)
1682 }
1683 Value::Vector(v) => serde_json::Value::Array(
1684 v.into_iter()
1685 .map(|f| {
1686 serde_json::Number::from_f64(f as f64)
1687 .map(serde_json::Value::Number)
1688 .unwrap_or(serde_json::Value::Null)
1689 })
1690 .collect(),
1691 ),
1692 Value::SparseVector { indices, values } => {
1693 let idx = serde_json::Value::Array(
1694 indices
1695 .into_iter()
1696 .map(|i| serde_json::Value::Number(serde_json::Number::from(i)))
1697 .collect(),
1698 );
1699 let vals = serde_json::Value::Array(
1700 values
1701 .into_iter()
1702 .map(|f| {
1703 serde_json::Number::from_f64(f as f64)
1704 .map(serde_json::Value::Number)
1705 .unwrap_or(serde_json::Value::Null)
1706 })
1707 .collect(),
1708 );
1709 let mut map = serde_json::Map::new();
1710 map.insert("indices".to_string(), idx);
1711 map.insert("values".to_string(), vals);
1712 serde_json::Value::Object(map)
1713 }
1714 Value::BinaryVector(bytes) => serde_json::Value::Array(
1717 bytes
1718 .into_iter()
1719 .map(|b| serde_json::Value::Number(serde_json::Number::from(b)))
1720 .collect(),
1721 ),
1722 Value::Temporal(t) => serde_json::Value::String(t.to_string()),
1723 }
1724 }
1725}
1726
1727#[macro_export]
1749macro_rules! unival {
1750 (null) => {
1752 $crate::Value::Null
1753 };
1754
1755 (true) => {
1757 $crate::Value::Bool(true)
1758 };
1759 (false) => {
1760 $crate::Value::Bool(false)
1761 };
1762
1763 ([ $($elem:tt),* $(,)? ]) => {
1765 $crate::Value::List(vec![ $( $crate::unival!($elem) ),* ])
1766 };
1767
1768 ({ $($key:tt : $val:tt),* $(,)? }) => {
1770 $crate::Value::Map({
1771 #[allow(unused_mut)]
1772 let mut map = ::std::collections::HashMap::new();
1773 $( map.insert(($key).to_string(), $crate::unival!($val)); )*
1774 map
1775 })
1776 };
1777
1778 ($e:expr) => {
1780 $crate::Value::from($e)
1781 };
1782}
1783
1784impl From<usize> for Value {
1789 fn from(v: usize) -> Self {
1790 Value::Int(v as i64)
1791 }
1792}
1793
1794impl From<u64> for Value {
1795 fn from(v: u64) -> Self {
1796 Value::Int(v as i64)
1797 }
1798}
1799
1800impl From<f32> for Value {
1801 fn from(v: f32) -> Self {
1802 Value::Float(v as f64)
1803 }
1804}
1805
1806#[cfg(test)]
1811mod tests {
1812 use super::*;
1813 use std::cmp::Ordering;
1814
1815 #[test]
1816 fn cmp_i64_f64_exact_above_2p53() {
1817 let two_p53 = 9_007_199_254_740_992.0_f64;
1819 assert_eq!(cmp_i64_f64(9_007_199_254_740_992, two_p53), Ordering::Equal);
1820 assert_eq!(
1821 cmp_i64_f64(9_007_199_254_740_993, two_p53),
1822 Ordering::Greater
1823 );
1824 assert_eq!(cmp_i64_f64(9_007_199_254_740_991, two_p53), Ordering::Less);
1825 }
1826
1827 #[test]
1828 fn cmp_i64_f64_small_and_fractional() {
1829 assert_eq!(cmp_i64_f64(2, 2.0), Ordering::Equal);
1830 assert_eq!(cmp_i64_f64(1, 1.5), Ordering::Less);
1831 assert_eq!(cmp_i64_f64(2, 1.5), Ordering::Greater);
1832 assert_eq!(cmp_i64_f64(-3, -2.5), Ordering::Less);
1833 assert_eq!(cmp_i64_f64(-2, -2.5), Ordering::Greater);
1834 assert_eq!(cmp_i64_f64(0, -0.0), Ordering::Equal);
1835 }
1836
1837 #[test]
1838 fn cmp_i64_f64_extremes_and_infinities() {
1839 assert_eq!(cmp_i64_f64(i64::MAX, f64::INFINITY), Ordering::Less);
1840 assert_eq!(cmp_i64_f64(i64::MIN, f64::NEG_INFINITY), Ordering::Greater);
1841 assert_eq!(
1843 cmp_i64_f64(i64::MAX, 9_223_372_036_854_775_808.0),
1844 Ordering::Less
1845 );
1846 assert_eq!(
1848 cmp_i64_f64(i64::MIN, -9_223_372_036_854_775_808.0),
1849 Ordering::Equal
1850 );
1851 assert_eq!(cmp_i64_f64(i64::MIN, -1e300), Ordering::Greater);
1853 assert_eq!(cmp_i64_f64(i64::MAX, 1e300), Ordering::Less);
1855 }
1856
1857 #[test]
1858 fn test_accessor_methods() {
1859 assert!(Value::Null.is_null());
1860 assert!(!Value::Int(1).is_null());
1861
1862 assert_eq!(Value::Bool(true).as_bool(), Some(true));
1863 assert_eq!(Value::Int(42).as_bool(), None);
1864
1865 assert_eq!(Value::Int(42).as_i64(), Some(42));
1866 assert_eq!(Value::Float(2.5).as_i64(), None);
1867
1868 assert_eq!(Value::Float(2.5).as_f64(), Some(2.5));
1870 assert_eq!(Value::Int(42).as_f64(), Some(42.0));
1871 assert_eq!(Value::String("x".into()).as_f64(), None);
1872
1873 assert_eq!(Value::String("hello".into()).as_str(), Some("hello"));
1874 assert_eq!(Value::Int(1).as_str(), None);
1875
1876 assert!(Value::Int(1).is_i64());
1877 assert!(!Value::Float(1.0).is_i64());
1878
1879 assert!(Value::Float(1.0).is_f64());
1880 assert!(!Value::Int(1).is_f64());
1881
1882 assert!(Value::Int(1).is_number());
1883 assert!(Value::Float(1.0).is_number());
1884 assert!(!Value::String("x".into()).is_number());
1885 }
1886
1887 #[test]
1888 fn test_serde_json_roundtrip() {
1889 let val = Value::Int(42);
1890 let json: serde_json::Value = val.clone().into();
1891 let back: Value = json.into();
1892 assert_eq!(val, back);
1893
1894 let val = Value::Float(2.5);
1895 let json: serde_json::Value = val.clone().into();
1896 let back: Value = json.into();
1897 assert_eq!(val, back);
1898
1899 let val = Value::String("hello".into());
1900 let json: serde_json::Value = val.clone().into();
1901 let back: Value = json.into();
1902 assert_eq!(val, back);
1903
1904 let val = Value::List(vec![Value::Int(1), Value::Int(2)]);
1905 let json: serde_json::Value = val.clone().into();
1906 let back: Value = json.into();
1907 assert_eq!(val, back);
1908 }
1909
1910 #[test]
1911 fn test_unival_macro() {
1912 assert_eq!(unival!(null), Value::Null);
1913 assert_eq!(unival!(true), Value::Bool(true));
1914 assert_eq!(unival!(false), Value::Bool(false));
1915 assert_eq!(unival!(42_i64), Value::Int(42));
1916 assert_eq!(unival!(2.5_f64), Value::Float(2.5));
1917 assert_eq!(unival!("hello"), Value::String("hello".into()));
1918
1919 let list = unival!([1_i64, 2_i64]);
1921 assert_eq!(list, Value::List(vec![Value::Int(1), Value::Int(2)]));
1922
1923 let map = unival!({"key": "val", "num": 42_i64});
1925 if let Value::Map(m) = &map {
1926 assert_eq!(m.get("key"), Some(&Value::String("val".into())));
1927 assert_eq!(m.get("num"), Some(&Value::Int(42)));
1928 } else {
1929 panic!("Expected Map");
1930 }
1931
1932 let x: i64 = 99;
1934 assert_eq!(unival!(x), Value::Int(99));
1935 }
1936
1937 #[test]
1938 fn test_int_float_distinction_preserved() {
1939 let int_val = Value::Int(42);
1941 let float_val = Value::Float(42.0);
1942
1943 assert!(int_val.is_i64());
1944 assert!(!int_val.is_f64());
1945
1946 assert!(float_val.is_f64());
1947 assert!(!float_val.is_i64());
1948
1949 assert_ne!(int_val, float_val);
1951 }
1952
1953 #[test]
1954 fn test_temporal_display_zero_seconds_omitted() {
1955 let lt = TemporalValue::LocalTime {
1957 nanos_since_midnight: 12 * 3600 * 1_000_000_000,
1958 };
1959 assert_eq!(lt.to_string(), "12:00");
1960
1961 let lt2 = TemporalValue::LocalTime {
1963 nanos_since_midnight: (12 * 3600 + 31 * 60 + 14) * 1_000_000_000,
1964 };
1965 assert_eq!(lt2.to_string(), "12:31:14");
1966
1967 let lt3 = TemporalValue::LocalTime {
1969 nanos_since_midnight: 500_000_000,
1970 };
1971 assert_eq!(lt3.to_string(), "00:00:00.5");
1972
1973 let t = TemporalValue::Time {
1975 nanos_since_midnight: 12 * 3600 * 1_000_000_000,
1976 offset_seconds: 0,
1977 };
1978 assert_eq!(t.to_string(), "12:00Z");
1979
1980 let t2 = TemporalValue::Time {
1982 nanos_since_midnight: (12 * 3600 + 31 * 60 + 14) * 1_000_000_000,
1983 offset_seconds: 3600,
1984 };
1985 assert_eq!(t2.to_string(), "12:31:14+01:00");
1986
1987 let epoch_nanos = chrono::NaiveDate::from_ymd_opt(1984, 10, 11)
1989 .unwrap()
1990 .and_hms_opt(12, 31, 0)
1991 .unwrap()
1992 .and_utc()
1993 .timestamp_nanos_opt()
1994 .unwrap();
1995 let ldt = TemporalValue::LocalDateTime {
1996 nanos_since_epoch: epoch_nanos,
1997 };
1998 assert_eq!(ldt.to_string(), "1984-10-11T12:31");
1999
2000 let utc_nanos = chrono::NaiveDate::from_ymd_opt(1984, 10, 11)
2002 .unwrap()
2003 .and_hms_opt(11, 31, 0)
2004 .unwrap()
2005 .and_utc()
2006 .timestamp_nanos_opt()
2007 .unwrap();
2008 let dt = TemporalValue::DateTime {
2009 nanos_since_epoch: utc_nanos,
2010 offset_seconds: 3600,
2011 timezone_name: None,
2012 };
2013 assert_eq!(dt.to_string(), "1984-10-11T12:31+01:00");
2014
2015 let utc_nanos2 = chrono::NaiveDate::from_ymd_opt(2015, 7, 21)
2017 .unwrap()
2018 .and_hms_nano_opt(20, 40, 32, 142_000_000)
2019 .unwrap()
2020 .and_utc()
2021 .timestamp_nanos_opt()
2022 .unwrap();
2023 let dt2 = TemporalValue::DateTime {
2024 nanos_since_epoch: utc_nanos2,
2025 offset_seconds: 3600,
2026 timezone_name: None,
2027 };
2028 assert_eq!(dt2.to_string(), "2015-07-21T21:40:32.142+01:00");
2029
2030 let utc_nanos3 = chrono::NaiveDate::from_ymd_opt(1984, 10, 11)
2032 .unwrap()
2033 .and_hms_opt(12, 31, 0)
2034 .unwrap()
2035 .and_utc()
2036 .timestamp_nanos_opt()
2037 .unwrap();
2038 let dt3 = TemporalValue::DateTime {
2039 nanos_since_epoch: utc_nanos3,
2040 offset_seconds: 0,
2041 timezone_name: None,
2042 };
2043 assert_eq!(dt3.to_string(), "1984-10-11T12:31Z");
2044 }
2045
2046 #[test]
2047 fn test_temporal_display_fractional_trailing_zeros_stripped() {
2048 let d = TemporalValue::Duration {
2050 months: 0,
2051 days: 0,
2052 nanos: 900_000_000,
2053 };
2054 assert_eq!(d.to_string(), "PT0.9S");
2055
2056 let d2 = TemporalValue::Duration {
2058 months: 0,
2059 days: 0,
2060 nanos: 400_000_000,
2061 };
2062 assert_eq!(d2.to_string(), "PT0.4S");
2063
2064 let d3 = TemporalValue::Duration {
2066 months: 0,
2067 days: 0,
2068 nanos: 142_000_000,
2069 };
2070 assert_eq!(d3.to_string(), "PT0.142S");
2071
2072 let d4 = TemporalValue::Duration {
2074 months: 0,
2075 days: 0,
2076 nanos: 1,
2077 };
2078 assert_eq!(d4.to_string(), "PT0.000000001S");
2079 }
2080
2081 #[test]
2082 fn test_temporal_display_offset_second_precision() {
2083 let t = TemporalValue::Time {
2085 nanos_since_midnight: 12 * 3600 * 1_000_000_000,
2086 offset_seconds: 2 * 3600 + 5 * 60 + 59,
2087 };
2088 assert_eq!(t.to_string(), "12:00+02:05:59");
2089
2090 let t2 = TemporalValue::Time {
2092 nanos_since_midnight: 12 * 3600 * 1_000_000_000,
2093 offset_seconds: -(2 * 3600 + 5 * 60 + 7),
2094 };
2095 assert_eq!(t2.to_string(), "12:00-02:05:07");
2096 }
2097
2098 #[test]
2099 fn test_temporal_display_datetime_with_timezone_name() {
2100 let utc_nanos = chrono::NaiveDate::from_ymd_opt(1984, 10, 11)
2101 .unwrap()
2102 .and_hms_opt(11, 31, 0)
2103 .unwrap()
2104 .and_utc()
2105 .timestamp_nanos_opt()
2106 .unwrap();
2107 let dt = TemporalValue::DateTime {
2108 nanos_since_epoch: utc_nanos,
2109 offset_seconds: 3600,
2110 timezone_name: Some("Europe/Stockholm".to_string()),
2111 };
2112 assert_eq!(dt.to_string(), "1984-10-11T12:31+01:00[Europe/Stockholm]");
2113 }
2114
2115 #[test]
2122 fn value_hash_eq_contract_float_signed_zero() {
2123 use std::collections::hash_map::DefaultHasher;
2124 use std::hash::{Hash, Hasher};
2125
2126 fn h(v: &Value) -> u64 {
2127 let mut s = DefaultHasher::new();
2128 v.hash(&mut s);
2129 s.finish()
2130 }
2131
2132 let pos = Value::Float(0.0);
2133 let neg = Value::Float(-0.0);
2134 assert_eq!(pos, neg, "0.0 and -0.0 compare equal");
2135 assert_eq!(
2136 h(&pos),
2137 h(&neg),
2138 "equal Values must hash equally (Hash/Eq contract)"
2139 );
2140 }
2141}