1use core::ops::Index;
9
10use bumpalo::Bump;
11
12#[cfg(feature = "datetime")]
13use crate::datetime::{DataDateTime, DataDuration};
14use crate::number::NumberValue;
15
16#[derive(Debug, Clone, Copy)]
19pub enum DataValue<'a> {
20 Null,
21 Bool(bool),
22 Number(NumberValue),
23 String(&'a str),
24 Array(&'a [DataValue<'a>]),
25 Object(&'a [(&'a str, DataValue<'a>)]),
26 #[cfg(feature = "datetime")]
30 DateTime(DataDateTime),
31 #[cfg(feature = "datetime")]
33 Duration(DataDuration),
34}
35
36pub(crate) static NULL: DataValue<'static> = DataValue::Null;
39
40impl<'a> DataValue<'a> {
41 #[inline]
44 pub fn null() -> Self {
45 DataValue::Null
46 }
47
48 #[inline]
49 pub fn bool(b: bool) -> Self {
50 DataValue::Bool(b)
51 }
52
53 #[inline]
54 pub fn from_i64(i: i64) -> Self {
55 DataValue::Number(NumberValue::from_i64(i))
56 }
57
58 #[inline]
59 pub fn from_f64(f: f64) -> Self {
60 DataValue::Number(NumberValue::from_f64(f))
61 }
62
63 #[inline]
64 pub fn from_str_in(s: &str, arena: &'a Bump) -> Self {
65 DataValue::String(arena.alloc_str(s))
66 }
67
68 #[inline]
71 pub fn from_borrowed_str(s: &'a str) -> Self {
72 DataValue::String(s)
73 }
74
75 #[inline]
78 pub fn is_null(&self) -> bool {
79 matches!(self, DataValue::Null)
80 }
81 #[inline]
82 pub fn is_bool(&self) -> bool {
83 matches!(self, DataValue::Bool(_))
84 }
85 #[inline]
86 pub fn is_number(&self) -> bool {
87 matches!(self, DataValue::Number(_))
88 }
89 #[inline]
90 pub fn is_i64(&self) -> bool {
91 matches!(self, DataValue::Number(NumberValue::Integer(_)))
92 }
93 #[inline]
94 pub fn is_f64(&self) -> bool {
95 matches!(self, DataValue::Number(NumberValue::Float(_)))
96 }
97 #[inline]
98 pub fn is_string(&self) -> bool {
99 matches!(self, DataValue::String(_))
100 }
101 #[inline]
102 pub fn is_array(&self) -> bool {
103 matches!(self, DataValue::Array(_))
104 }
105 #[inline]
106 pub fn is_object(&self) -> bool {
107 matches!(self, DataValue::Object(_))
108 }
109
110 #[cfg(feature = "datetime")]
111 #[inline]
112 pub fn is_datetime(&self) -> bool {
113 matches!(self, DataValue::DateTime(_))
114 }
115 #[cfg(feature = "datetime")]
116 #[inline]
117 pub fn is_duration(&self) -> bool {
118 matches!(self, DataValue::Duration(_))
119 }
120
121 #[inline]
124 pub fn as_bool(&self) -> Option<bool> {
125 match self {
126 DataValue::Bool(b) => Some(*b),
127 _ => None,
128 }
129 }
130
131 #[inline]
132 pub fn as_i64(&self) -> Option<i64> {
133 match self {
134 DataValue::Number(n) => n.as_i64(),
135 _ => None,
136 }
137 }
138
139 #[inline]
140 pub fn as_f64(&self) -> Option<f64> {
141 match self {
142 DataValue::Number(n) => Some(n.as_f64()),
143 _ => None,
144 }
145 }
146
147 #[inline]
148 pub fn as_number(&self) -> Option<&NumberValue> {
149 match self {
150 DataValue::Number(n) => Some(n),
151 _ => None,
152 }
153 }
154
155 #[inline]
156 pub fn as_str(&self) -> Option<&'a str> {
157 match *self {
158 DataValue::String(s) => Some(s),
159 _ => None,
160 }
161 }
162
163 #[inline]
164 pub fn as_array(&self) -> Option<&'a [DataValue<'a>]> {
165 match *self {
166 DataValue::Array(a) => Some(a),
167 _ => None,
168 }
169 }
170
171 #[inline]
172 pub fn as_object(&self) -> Option<&'a [(&'a str, DataValue<'a>)]> {
173 match *self {
174 DataValue::Object(o) => Some(o),
175 _ => None,
176 }
177 }
178
179 #[cfg(feature = "datetime")]
180 #[inline]
181 pub fn as_datetime(&self) -> Option<&DataDateTime> {
182 match self {
183 DataValue::DateTime(d) => Some(d),
184 _ => None,
185 }
186 }
187
188 #[cfg(feature = "datetime")]
189 #[inline]
190 pub fn as_duration(&self) -> Option<&DataDuration> {
191 match self {
192 DataValue::Duration(d) => Some(d),
193 _ => None,
194 }
195 }
196
197 #[cfg(feature = "datetime")]
198 #[inline]
199 pub fn datetime(dt: DataDateTime) -> Self {
200 DataValue::DateTime(dt)
201 }
202
203 #[cfg(feature = "datetime")]
204 #[inline]
205 pub fn duration(d: DataDuration) -> Self {
206 DataValue::Duration(d)
207 }
208
209 #[inline]
212 pub fn get<I: ValueIndex>(&self, index: I) -> Option<&DataValue<'a>> {
213 I::index_into(&index, self)
214 }
215
216 #[inline]
218 pub fn len(&self) -> Option<usize> {
219 match self {
220 DataValue::Array(a) => Some(a.len()),
221 DataValue::Object(o) => Some(o.len()),
222 _ => None,
223 }
224 }
225
226 #[inline]
227 pub fn is_empty(&self) -> Option<bool> {
228 self.len().map(|n| n == 0)
229 }
230
231 #[inline]
234 pub fn members(&self) -> core::slice::Iter<'_, DataValue<'a>> {
235 match *self {
236 DataValue::Array(items) => items.iter(),
237 _ => [].iter(),
238 }
239 }
240
241 #[inline]
244 pub fn entries(&self) -> EntriesIter<'_, 'a> {
245 match *self {
246 DataValue::Object(pairs) => EntriesIter {
247 inner: pairs.iter(),
248 },
249 _ => EntriesIter { inner: [].iter() },
250 }
251 }
252
253 #[inline]
257 pub fn to_json_string(&self) -> String {
258 self.to_string()
259 }
260}
261
262pub struct EntriesIter<'v, 'a> {
265 inner: core::slice::Iter<'v, (&'a str, DataValue<'a>)>,
266}
267
268impl<'v, 'a> Iterator for EntriesIter<'v, 'a> {
269 type Item = (&'a str, &'v DataValue<'a>);
270 #[inline]
271 fn next(&mut self) -> Option<Self::Item> {
272 self.inner.next().map(|(k, v)| (*k, v))
273 }
274 #[inline]
275 fn size_hint(&self) -> (usize, Option<usize>) {
276 self.inner.size_hint()
277 }
278}
279
280impl ExactSizeIterator for EntriesIter<'_, '_> {}
281
282impl Default for DataValue<'_> {
283 #[inline]
284 fn default() -> Self {
285 DataValue::Null
286 }
287}
288
289impl<'a> PartialEq for DataValue<'a> {
290 #[inline]
291 fn eq(&self, other: &Self) -> bool {
292 match (self, other) {
293 (DataValue::Null, DataValue::Null) => true,
294 (DataValue::Bool(a), DataValue::Bool(b)) => a == b,
295 (DataValue::Number(a), DataValue::Number(b)) => a == b,
296 (DataValue::String(a), DataValue::String(b)) => a == b,
297 (DataValue::Array(a), DataValue::Array(b)) => a == b,
298 (DataValue::Object(a), DataValue::Object(b)) => {
299 if a.len() != b.len() {
300 return false;
301 }
302 a.iter().all(|(k, v)| {
304 b.iter()
305 .find(|(bk, _)| bk == k)
306 .is_some_and(|(_, bv)| v == bv)
307 })
308 }
309 #[cfg(feature = "datetime")]
310 (DataValue::DateTime(a), DataValue::DateTime(b)) => a == b,
311 #[cfg(feature = "datetime")]
312 (DataValue::Duration(a), DataValue::Duration(b)) => a == b,
313 _ => false,
314 }
315 }
316}
317
318pub trait ValueIndex: private::Sealed {
323 fn index_into<'v, 'a>(&self, value: &'v DataValue<'a>) -> Option<&'v DataValue<'a>>;
324 fn index_into_or_null<'v, 'a>(&self, value: &'v DataValue<'a>) -> &'v DataValue<'a>;
325}
326
327mod private {
328 pub trait Sealed {}
329 impl Sealed for str {}
330 impl Sealed for String {}
331 impl Sealed for usize {}
332 impl<T: Sealed + ?Sized> Sealed for &T {}
333}
334
335impl ValueIndex for str {
336 #[inline]
337 fn index_into<'v, 'a>(&self, value: &'v DataValue<'a>) -> Option<&'v DataValue<'a>> {
338 match value {
339 DataValue::Object(pairs) => pairs.iter().find(|(k, _)| *k == self).map(|(_, v)| v),
340 _ => None,
341 }
342 }
343 #[inline]
344 fn index_into_or_null<'v, 'a>(&self, value: &'v DataValue<'a>) -> &'v DataValue<'a> {
345 self.index_into(value).unwrap_or(&NULL)
348 }
349}
350
351impl ValueIndex for String {
352 #[inline]
353 fn index_into<'v, 'a>(&self, value: &'v DataValue<'a>) -> Option<&'v DataValue<'a>> {
354 self.as_str().index_into(value)
355 }
356 #[inline]
357 fn index_into_or_null<'v, 'a>(&self, value: &'v DataValue<'a>) -> &'v DataValue<'a> {
358 self.as_str().index_into_or_null(value)
359 }
360}
361
362impl ValueIndex for usize {
363 #[inline]
364 fn index_into<'v, 'a>(&self, value: &'v DataValue<'a>) -> Option<&'v DataValue<'a>> {
365 match value {
366 DataValue::Array(items) => items.get(*self),
367 _ => None,
368 }
369 }
370 #[inline]
371 fn index_into_or_null<'v, 'a>(&self, value: &'v DataValue<'a>) -> &'v DataValue<'a> {
372 self.index_into(value).unwrap_or(&NULL)
373 }
374}
375
376impl<T: ValueIndex + ?Sized> ValueIndex for &T {
377 #[inline]
378 fn index_into<'v, 'a>(&self, value: &'v DataValue<'a>) -> Option<&'v DataValue<'a>> {
379 (**self).index_into(value)
380 }
381 #[inline]
382 fn index_into_or_null<'v, 'a>(&self, value: &'v DataValue<'a>) -> &'v DataValue<'a> {
383 (**self).index_into_or_null(value)
384 }
385}
386
387impl<'a, I: ValueIndex> Index<I> for DataValue<'a> {
388 type Output = DataValue<'a>;
389 #[inline]
390 fn index(&self, index: I) -> &DataValue<'a> {
391 index.index_into_or_null(self)
392 }
393}
394
395#[cfg(test)]
396mod tests {
397 use super::*;
398
399 fn sample<'a>(arena: &'a Bump) -> DataValue<'a> {
400 let nested = arena.alloc_slice_copy(&[
401 DataValue::from_i64(1),
402 DataValue::from_i64(2),
403 DataValue::from_i64(3),
404 ]);
405 let inner_obj = arena.alloc_slice_copy(&[("k", DataValue::Bool(true))]);
406 let pairs = arena.alloc_slice_copy(&[
407 ("name", DataValue::from_borrowed_str("alice")),
408 ("nums", DataValue::Array(nested)),
409 ("inner", DataValue::Object(inner_obj)),
410 ]);
411 DataValue::Object(pairs)
412 }
413
414 #[test]
415 fn get_object_key() {
416 let arena = Bump::new();
417 let v = sample(&arena);
418 assert_eq!(v.get("name").and_then(|x| x.as_str()), Some("alice"));
419 assert!(v.get("missing").is_none());
420 }
421
422 #[test]
423 fn get_array_index() {
424 let arena = Bump::new();
425 let v = sample(&arena);
426 let nums = v.get("nums").unwrap();
427 assert_eq!(nums.get(0).and_then(|x| x.as_i64()), Some(1));
428 assert_eq!(nums.get(2).and_then(|x| x.as_i64()), Some(3));
429 assert!(nums.get(99).is_none());
430 }
431
432 #[test]
433 fn index_returns_null_for_missing() {
434 let arena = Bump::new();
435 let v = sample(&arena);
436 assert!(v["missing"].is_null());
437 assert!(v["nums"][99].is_null());
438 }
439
440 #[test]
441 fn chained_index() {
442 let arena = Bump::new();
443 let v = sample(&arena);
444 assert_eq!(v["inner"]["k"].as_bool(), Some(true));
445 }
446
447 #[test]
448 fn predicates_and_len() {
449 let arena = Bump::new();
450 let v = sample(&arena);
451 assert!(v.is_object());
452 assert_eq!(v.len(), Some(3));
453 assert_eq!(v["nums"].len(), Some(3));
454 assert_eq!(v["name"].len(), None);
455 }
456
457 #[cfg(feature = "datetime")]
458 #[test]
459 fn datetime_variant_round_trips_through_value() {
460 use crate::datetime::{DataDateTime, DataDuration};
461 let dt = DataDateTime::parse("2024-01-15T12:30:45Z").unwrap();
462 let v = DataValue::datetime(dt);
463 assert!(v.is_datetime());
464 assert_eq!(
465 v.as_datetime().map(|d| d.to_iso_string()).as_deref(),
466 Some("2024-01-15T12:30:45Z")
467 );
468
469 let dur = DataDuration::parse("1d:2h").unwrap();
470 let v2 = DataValue::duration(dur);
471 assert!(v2.is_duration());
472 assert_eq!(v2.as_duration().unwrap().to_string(), "1d:2h:0m:0s");
473
474 assert_eq!(v, DataValue::datetime(dt));
476 assert_ne!(v, v2);
477 }
478
479 #[test]
480 fn equality_object_order_insensitive() {
481 let arena = Bump::new();
482 let a =
483 arena.alloc_slice_copy(&[("x", DataValue::from_i64(1)), ("y", DataValue::from_i64(2))]);
484 let b =
485 arena.alloc_slice_copy(&[("y", DataValue::from_i64(2)), ("x", DataValue::from_i64(1))]);
486 assert_eq!(DataValue::Object(a), DataValue::Object(b));
487 }
488}