1use core::ops::Index;
9
10use bumpalo::Bump;
11
12#[cfg(feature = "datetime")]
13use crate::datetime::{DataDateTime, DataDuration};
14use crate::number::NumberValue;
15#[cfg(feature = "tensor")]
16use crate::tensor::DataTensor;
17
18#[derive(Debug, Clone, Copy)]
21pub enum DataValue<'a> {
22 Null,
23 Bool(bool),
24 Number(NumberValue),
25 String(&'a str),
26 Array(&'a [DataValue<'a>]),
27 Object(&'a [(&'a str, DataValue<'a>)]),
28 #[cfg(feature = "datetime")]
32 DateTime(DataDateTime),
33 #[cfg(feature = "datetime")]
35 Duration(DataDuration),
36 #[cfg(feature = "tensor")]
40 Tensor(&'a DataTensor<'a>),
41}
42
43pub(crate) static NULL: DataValue<'static> = DataValue::Null;
46
47#[cfg(target_pointer_width = "64")]
50const _: () = assert!(core::mem::size_of::<DataValue<'static>>() == 24);
51
52impl<'a> DataValue<'a> {
53 #[inline]
56 pub fn null() -> Self {
57 DataValue::Null
58 }
59
60 #[inline]
61 pub fn bool(b: bool) -> Self {
62 DataValue::Bool(b)
63 }
64
65 #[inline]
66 pub fn from_i64(i: i64) -> Self {
67 DataValue::Number(NumberValue::from_i64(i))
68 }
69
70 #[inline]
71 pub fn from_f64(f: f64) -> Self {
72 DataValue::Number(NumberValue::from_f64(f))
73 }
74
75 #[inline]
76 pub fn from_str_in(s: &str, arena: &'a Bump) -> Self {
77 DataValue::String(arena.alloc_str(s))
78 }
79
80 #[inline]
83 pub fn from_borrowed_str(s: &'a str) -> Self {
84 DataValue::String(s)
85 }
86
87 #[inline]
90 pub fn is_null(&self) -> bool {
91 matches!(self, DataValue::Null)
92 }
93 #[inline]
94 pub fn is_bool(&self) -> bool {
95 matches!(self, DataValue::Bool(_))
96 }
97 #[inline]
98 pub fn is_number(&self) -> bool {
99 matches!(self, DataValue::Number(_))
100 }
101 #[inline]
102 pub fn is_i64(&self) -> bool {
103 matches!(self, DataValue::Number(NumberValue::Integer(_)))
104 }
105 #[inline]
106 pub fn is_f64(&self) -> bool {
107 matches!(self, DataValue::Number(NumberValue::Float(_)))
108 }
109 #[inline]
110 pub fn is_string(&self) -> bool {
111 matches!(self, DataValue::String(_))
112 }
113 #[inline]
114 pub fn is_array(&self) -> bool {
115 matches!(self, DataValue::Array(_))
116 }
117 #[inline]
118 pub fn is_object(&self) -> bool {
119 matches!(self, DataValue::Object(_))
120 }
121
122 #[cfg(feature = "datetime")]
123 #[inline]
124 pub fn is_datetime(&self) -> bool {
125 matches!(self, DataValue::DateTime(_))
126 }
127 #[cfg(feature = "datetime")]
128 #[inline]
129 pub fn is_duration(&self) -> bool {
130 matches!(self, DataValue::Duration(_))
131 }
132 #[cfg(feature = "tensor")]
133 #[inline]
134 pub fn is_tensor(&self) -> bool {
135 matches!(self, DataValue::Tensor(_))
136 }
137
138 #[inline]
141 pub fn as_bool(&self) -> Option<bool> {
142 match self {
143 DataValue::Bool(b) => Some(*b),
144 _ => None,
145 }
146 }
147
148 #[inline]
149 pub fn as_i64(&self) -> Option<i64> {
150 match self {
151 DataValue::Number(n) => n.as_i64(),
152 _ => None,
153 }
154 }
155
156 #[inline]
157 pub fn as_f64(&self) -> Option<f64> {
158 match self {
159 DataValue::Number(n) => Some(n.as_f64()),
160 _ => None,
161 }
162 }
163
164 #[inline]
165 pub fn as_number(&self) -> Option<&NumberValue> {
166 match self {
167 DataValue::Number(n) => Some(n),
168 _ => None,
169 }
170 }
171
172 #[inline]
173 pub fn as_str(&self) -> Option<&'a str> {
174 match *self {
175 DataValue::String(s) => Some(s),
176 _ => None,
177 }
178 }
179
180 #[inline]
181 pub fn as_array(&self) -> Option<&'a [DataValue<'a>]> {
182 match *self {
183 DataValue::Array(a) => Some(a),
184 _ => None,
185 }
186 }
187
188 #[inline]
189 pub fn as_object(&self) -> Option<&'a [(&'a str, DataValue<'a>)]> {
190 match *self {
191 DataValue::Object(o) => Some(o),
192 _ => None,
193 }
194 }
195
196 #[cfg(feature = "datetime")]
197 #[inline]
198 pub fn as_datetime(&self) -> Option<&DataDateTime> {
199 match self {
200 DataValue::DateTime(d) => Some(d),
201 _ => None,
202 }
203 }
204
205 #[cfg(feature = "datetime")]
206 #[inline]
207 pub fn as_duration(&self) -> Option<&DataDuration> {
208 match self {
209 DataValue::Duration(d) => Some(d),
210 _ => None,
211 }
212 }
213
214 #[cfg(feature = "datetime")]
215 #[inline]
216 pub fn datetime(dt: DataDateTime) -> Self {
217 DataValue::DateTime(dt)
218 }
219
220 #[cfg(feature = "datetime")]
221 #[inline]
222 pub fn duration(d: DataDuration) -> Self {
223 DataValue::Duration(d)
224 }
225
226 #[cfg(feature = "tensor")]
227 #[inline]
228 pub fn as_tensor(&self) -> Option<&'a DataTensor<'a>> {
229 match *self {
230 DataValue::Tensor(t) => Some(t),
231 _ => None,
232 }
233 }
234
235 #[cfg(feature = "tensor")]
238 #[inline]
239 pub fn tensor(t: &'a DataTensor<'a>) -> Self {
240 DataValue::Tensor(t)
241 }
242
243 #[cfg(feature = "tensor")]
245 #[inline]
246 pub fn tensor_in(t: DataTensor<'a>, arena: &'a Bump) -> Self {
247 DataValue::Tensor(arena.alloc(t))
248 }
249
250 #[inline]
253 pub fn get<I: ValueIndex>(&self, index: I) -> Option<&DataValue<'a>> {
254 I::index_into(&index, self)
255 }
256
257 #[inline]
259 pub fn len(&self) -> Option<usize> {
260 match self {
261 DataValue::Array(a) => Some(a.len()),
262 DataValue::Object(o) => Some(o.len()),
263 _ => None,
264 }
265 }
266
267 #[inline]
268 pub fn is_empty(&self) -> Option<bool> {
269 self.len().map(|n| n == 0)
270 }
271
272 #[inline]
275 pub fn members(&self) -> core::slice::Iter<'_, DataValue<'a>> {
276 match *self {
277 DataValue::Array(items) => items.iter(),
278 _ => [].iter(),
279 }
280 }
281
282 #[inline]
285 pub fn entries(&self) -> EntriesIter<'_, 'a> {
286 match *self {
287 DataValue::Object(pairs) => EntriesIter {
288 inner: pairs.iter(),
289 },
290 _ => EntriesIter { inner: [].iter() },
291 }
292 }
293
294 #[inline]
298 pub fn to_json_string(&self) -> String {
299 self.to_string()
300 }
301}
302
303pub struct EntriesIter<'v, 'a> {
306 inner: core::slice::Iter<'v, (&'a str, DataValue<'a>)>,
307}
308
309impl<'v, 'a> Iterator for EntriesIter<'v, 'a> {
310 type Item = (&'a str, &'v DataValue<'a>);
311 #[inline]
312 fn next(&mut self) -> Option<Self::Item> {
313 self.inner.next().map(|(k, v)| (*k, v))
314 }
315 #[inline]
316 fn size_hint(&self) -> (usize, Option<usize>) {
317 self.inner.size_hint()
318 }
319}
320
321impl ExactSizeIterator for EntriesIter<'_, '_> {}
322
323impl Default for DataValue<'_> {
324 #[inline]
325 fn default() -> Self {
326 DataValue::Null
327 }
328}
329
330impl<'a> PartialEq for DataValue<'a> {
331 #[inline]
332 fn eq(&self, other: &Self) -> bool {
333 match (self, other) {
334 (DataValue::Null, DataValue::Null) => true,
335 (DataValue::Bool(a), DataValue::Bool(b)) => a == b,
336 (DataValue::Number(a), DataValue::Number(b)) => a == b,
337 (DataValue::String(a), DataValue::String(b)) => a == b,
338 (DataValue::Array(a), DataValue::Array(b)) => a == b,
339 (DataValue::Object(a), DataValue::Object(b)) => {
340 if a.len() != b.len() {
341 return false;
342 }
343 a.iter().all(|(k, v)| {
345 b.iter()
346 .find(|(bk, _)| bk == k)
347 .is_some_and(|(_, bv)| v == bv)
348 })
349 }
350 #[cfg(feature = "datetime")]
351 (DataValue::DateTime(a), DataValue::DateTime(b)) => a == b,
352 #[cfg(feature = "datetime")]
353 (DataValue::Duration(a), DataValue::Duration(b)) => a == b,
354 #[cfg(feature = "tensor")]
355 (DataValue::Tensor(a), DataValue::Tensor(b)) => a == b,
356 _ => false,
357 }
358 }
359}
360
361pub trait ValueIndex: private::Sealed {
366 fn index_into<'v, 'a>(&self, value: &'v DataValue<'a>) -> Option<&'v DataValue<'a>>;
367 fn index_into_or_null<'v, 'a>(&self, value: &'v DataValue<'a>) -> &'v DataValue<'a>;
368}
369
370mod private {
371 pub trait Sealed {}
372 impl Sealed for str {}
373 impl Sealed for String {}
374 impl Sealed for usize {}
375 impl<T: Sealed + ?Sized> Sealed for &T {}
376}
377
378impl ValueIndex for str {
379 #[inline]
380 fn index_into<'v, 'a>(&self, value: &'v DataValue<'a>) -> Option<&'v DataValue<'a>> {
381 match value {
382 DataValue::Object(pairs) => pairs.iter().find(|(k, _)| *k == self).map(|(_, v)| v),
383 _ => None,
384 }
385 }
386 #[inline]
387 fn index_into_or_null<'v, 'a>(&self, value: &'v DataValue<'a>) -> &'v DataValue<'a> {
388 self.index_into(value).unwrap_or(&NULL)
391 }
392}
393
394impl ValueIndex for String {
395 #[inline]
396 fn index_into<'v, 'a>(&self, value: &'v DataValue<'a>) -> Option<&'v DataValue<'a>> {
397 self.as_str().index_into(value)
398 }
399 #[inline]
400 fn index_into_or_null<'v, 'a>(&self, value: &'v DataValue<'a>) -> &'v DataValue<'a> {
401 self.as_str().index_into_or_null(value)
402 }
403}
404
405impl ValueIndex for usize {
406 #[inline]
407 fn index_into<'v, 'a>(&self, value: &'v DataValue<'a>) -> Option<&'v DataValue<'a>> {
408 match value {
409 DataValue::Array(items) => items.get(*self),
410 _ => None,
411 }
412 }
413 #[inline]
414 fn index_into_or_null<'v, 'a>(&self, value: &'v DataValue<'a>) -> &'v DataValue<'a> {
415 self.index_into(value).unwrap_or(&NULL)
416 }
417}
418
419impl<T: ValueIndex + ?Sized> ValueIndex for &T {
420 #[inline]
421 fn index_into<'v, 'a>(&self, value: &'v DataValue<'a>) -> Option<&'v DataValue<'a>> {
422 (**self).index_into(value)
423 }
424 #[inline]
425 fn index_into_or_null<'v, 'a>(&self, value: &'v DataValue<'a>) -> &'v DataValue<'a> {
426 (**self).index_into_or_null(value)
427 }
428}
429
430impl<'a, I: ValueIndex> Index<I> for DataValue<'a> {
431 type Output = DataValue<'a>;
432 #[inline]
433 fn index(&self, index: I) -> &DataValue<'a> {
434 index.index_into_or_null(self)
435 }
436}
437
438#[cfg(test)]
439mod tests {
440 use super::*;
441
442 fn sample<'a>(arena: &'a Bump) -> DataValue<'a> {
443 let nested = arena.alloc_slice_copy(&[
444 DataValue::from_i64(1),
445 DataValue::from_i64(2),
446 DataValue::from_i64(3),
447 ]);
448 let inner_obj = arena.alloc_slice_copy(&[("k", DataValue::Bool(true))]);
449 let pairs = arena.alloc_slice_copy(&[
450 ("name", DataValue::from_borrowed_str("alice")),
451 ("nums", DataValue::Array(nested)),
452 ("inner", DataValue::Object(inner_obj)),
453 ]);
454 DataValue::Object(pairs)
455 }
456
457 #[test]
458 fn get_object_key() {
459 let arena = Bump::new();
460 let v = sample(&arena);
461 assert_eq!(v.get("name").and_then(|x| x.as_str()), Some("alice"));
462 assert!(v.get("missing").is_none());
463 }
464
465 #[test]
466 fn get_array_index() {
467 let arena = Bump::new();
468 let v = sample(&arena);
469 let nums = v.get("nums").unwrap();
470 assert_eq!(nums.get(0).and_then(|x| x.as_i64()), Some(1));
471 assert_eq!(nums.get(2).and_then(|x| x.as_i64()), Some(3));
472 assert!(nums.get(99).is_none());
473 }
474
475 #[test]
476 fn index_returns_null_for_missing() {
477 let arena = Bump::new();
478 let v = sample(&arena);
479 assert!(v["missing"].is_null());
480 assert!(v["nums"][99].is_null());
481 }
482
483 #[test]
484 fn chained_index() {
485 let arena = Bump::new();
486 let v = sample(&arena);
487 assert_eq!(v["inner"]["k"].as_bool(), Some(true));
488 }
489
490 #[test]
491 fn predicates_and_len() {
492 let arena = Bump::new();
493 let v = sample(&arena);
494 assert!(v.is_object());
495 assert_eq!(v.len(), Some(3));
496 assert_eq!(v["nums"].len(), Some(3));
497 assert_eq!(v["name"].len(), None);
498 }
499
500 #[cfg(feature = "datetime")]
501 #[test]
502 fn datetime_variant_round_trips_through_value() {
503 use crate::datetime::{DataDateTime, DataDuration};
504 let dt = DataDateTime::parse("2024-01-15T12:30:45Z").unwrap();
505 let v = DataValue::datetime(dt);
506 assert!(v.is_datetime());
507 assert_eq!(
508 v.as_datetime().map(|d| d.to_iso_string()).as_deref(),
509 Some("2024-01-15T12:30:45Z")
510 );
511
512 let dur = DataDuration::parse("1d:2h").unwrap();
513 let v2 = DataValue::duration(dur);
514 assert!(v2.is_duration());
515 assert_eq!(v2.as_duration().unwrap().to_string(), "1d:2h:0m:0s");
516
517 assert_eq!(v, DataValue::datetime(dt));
519 assert_ne!(v, v2);
520 }
521
522 #[test]
523 fn equality_object_order_insensitive() {
524 let arena = Bump::new();
525 let a =
526 arena.alloc_slice_copy(&[("x", DataValue::from_i64(1)), ("y", DataValue::from_i64(2))]);
527 let b =
528 arena.alloc_slice_copy(&[("y", DataValue::from_i64(2)), ("x", DataValue::from_i64(1))]);
529 assert_eq!(DataValue::Object(a), DataValue::Object(b));
530 }
531
532 #[cfg(feature = "tensor")]
533 #[test]
534 fn tensor_variant_is_opaque_and_copy() {
535 use crate::tensor::DataTensor;
536 let arena = Bump::new();
537 let t = DataTensor::from_slice_in(&[2, 2], &[1.0f32, 2.0, 3.0, 4.0], &arena).unwrap();
538 let v = DataValue::tensor_in(t, &arena);
539 let copy = v; assert!(v.is_tensor());
541 assert_eq!(v.as_tensor().unwrap().shape(), &[2, 2]);
542 assert!(v.as_array().is_none());
543 assert!(v.get(0).is_none());
544 assert!(v.get("x").is_none());
545 assert!(v[0].is_null());
546 assert_eq!(v.len(), None);
547 assert_eq!(v.members().count(), 0);
548 assert_eq!(v.entries().count(), 0);
549 assert_eq!(v, copy);
550 let other = DataValue::tensor_in(
551 DataTensor::from_slice_in(&[4], &[1.0f32, 2.0, 3.0, 4.0], &arena).unwrap(),
552 &arena,
553 );
554 assert_ne!(v, other, "shape participates in equality");
555 assert_ne!(v, DataValue::Null);
556 }
557}