Skip to main content

structfs_core_store/
value.rs

1//! The Value type - a tree-shaped data structure.
2//!
3//! This is the "struct" in StructFS. It's a dynamically-typed tree that can
4//! represent any structured data: JSON, MessagePack, CBOR, protobuf (with schema), etc.
5
6use std::collections::BTreeMap;
7
8use crate::{Error, Path, PathError};
9
10/// A tree-shaped value that can be read from or written to a Store.
11///
12/// This is the universal data representation in StructFS. It maps directly
13/// to JSON, MessagePack, CBOR, etc., but is encoding-agnostic.
14///
15/// # Design Notes
16///
17/// - Uses `BTreeMap` for deterministic UTF-8 key ordering
18/// - Includes `Bytes` for binary data (unlike JSON, but like CBOR/MessagePack)
19/// - Integers cover the union of i64 and u64; signedness is not semantic
20#[derive(Clone, Debug, Default, PartialEq)]
21#[non_exhaustive]
22pub enum Value {
23    /// A present null value. Distinct from "path doesn't exist".
24    #[default]
25    Null,
26    /// Boolean value.
27    Bool(bool),
28    /// Signed 64-bit integer.
29    Integer(i64),
30    /// Nonnegative integer storage; normalized values use this only above i64::MAX.
31    Unsigned(u64),
32    /// 64-bit floating point.
33    Float(f64),
34    /// UTF-8 string.
35    String(String),
36    /// Binary data (for formats that support it: CBOR, MessagePack, etc.)
37    Bytes(Vec<u8>),
38    /// Ordered sequence of values.
39    Array(Vec<Value>),
40    /// Key-value map with string keys (the "struct" part).
41    Map(BTreeMap<String, Value>),
42}
43
44impl Value {
45    /// Normalize signedness and NaNs recursively. Ordinary `PartialEq` is unchanged.
46    pub fn normalize(&mut self) {
47        match self {
48            Self::Unsigned(n) if *n <= i64::MAX as u64 => *self = Self::Integer(*n as i64),
49            Self::Float(f) if f.is_nan() => *f = f64::from_bits(0x7ff8_0000_0000_0000),
50            Self::Array(a) => a.iter_mut().for_each(Self::normalize),
51            Self::Map(m) => m.values_mut().for_each(Self::normalize),
52            _ => {}
53        }
54    }
55
56    /// Equality defined by StructFS Value v1, including NaN equality and signed zero.
57    pub fn semantic_eq(&self, other: &Self) -> bool {
58        match (self, other) {
59            (Self::Integer(a), Self::Unsigned(b)) | (Self::Unsigned(b), Self::Integer(a)) => {
60                *a >= 0 && *a as u64 == *b
61            }
62            (Self::Float(a), Self::Float(b)) => {
63                (a.is_nan() && b.is_nan()) || a.to_bits() == b.to_bits()
64            }
65            (Self::Array(a), Self::Array(b)) => {
66                a.len() == b.len() && a.iter().zip(b).all(|(a, b)| a.semantic_eq(b))
67            }
68            (Self::Map(a), Self::Map(b)) => {
69                a.len() == b.len()
70                    && a.iter()
71                        .zip(b)
72                        .all(|((k, a), (l, b))| k == l && a.semantic_eq(b))
73            }
74            _ => self == other,
75        }
76    }
77
78    /// Create a null value.
79    pub fn null() -> Self {
80        Value::Null
81    }
82
83    /// Create an empty map.
84    pub fn map() -> Self {
85        Value::Map(BTreeMap::new())
86    }
87
88    /// Create an empty array.
89    pub fn array() -> Self {
90        Value::Array(Vec::new())
91    }
92
93    /// Check if this value is null.
94    pub fn is_null(&self) -> bool {
95        matches!(self, Value::Null)
96    }
97
98    /// Check if this value is a map (struct).
99    pub fn is_map(&self) -> bool {
100        matches!(self, Value::Map(_))
101    }
102
103    /// Check if this value is an array.
104    pub fn is_array(&self) -> bool {
105        matches!(self, Value::Array(_))
106    }
107
108    /// Get a reference to a nested value by path.
109    ///
110    /// Returns `None` if the path doesn't exist or can't be navigated
111    /// (e.g., trying to index into a string).
112    pub fn get(&self, path: &Path) -> Option<&Value> {
113        let mut current = self;
114        for component in path.iter() {
115            current = match current {
116                Value::Map(map) => map.get(component)?,
117                Value::Array(arr) => {
118                    let index: usize = component.parse().ok()?;
119                    arr.get(index)?
120                }
121                _ => return None,
122            };
123        }
124        Some(current)
125    }
126
127    /// Get a mutable reference to a nested value by path.
128    pub fn get_mut(&mut self, path: &Path) -> Option<&mut Value> {
129        let mut current = self;
130        for component in path.iter() {
131            current = match current {
132                Value::Map(map) => map.get_mut(component)?,
133                Value::Array(arr) => {
134                    let index: usize = component.parse().ok()?;
135                    arr.get_mut(index)?
136                }
137                _ => return None,
138            };
139        }
140        Some(current)
141    }
142
143    /// Set a value at a path, creating intermediate maps as needed.
144    ///
145    /// # Errors
146    ///
147    /// Returns an error if the path traverses through a non-container value
148    /// (e.g., trying to set `foo/bar` when `foo` is a string).
149    pub fn set(&mut self, path: &Path, value: Value) -> Result<(), Error> {
150        if path.is_empty() {
151            *self = value;
152            return Ok(());
153        }
154
155        let mut current = self;
156
157        // Navigate to parent, creating intermediate maps
158        for (i, component) in path.iter().enumerate() {
159            let is_last = i == path.len() - 1;
160
161            if is_last {
162                // Set the value at the last component
163                match current {
164                    Value::Map(map) => {
165                        map.insert(component.to_string(), value);
166                        return Ok(());
167                    }
168                    Value::Array(arr) => {
169                        let index: usize = component.parse().map_err(|_| {
170                            Error::Path(PathError::InvalidPath {
171                                message: format!("invalid array index: {}", component),
172                            })
173                        })?;
174                        if index < arr.len() {
175                            arr[index] = value;
176                        } else if index == arr.len() {
177                            arr.push(value);
178                        } else {
179                            return Err(Error::Path(PathError::InvalidPath {
180                                message: format!("array index {} out of bounds", index),
181                            }));
182                        }
183                        return Ok(());
184                    }
185                    _ => {
186                        return Err(Error::Path(PathError::InvalidPath {
187                            message: format!(
188                                "cannot set child '{}' on non-container value",
189                                component
190                            ),
191                        }));
192                    }
193                }
194            } else {
195                // Navigate or create intermediate map
196                match current {
197                    Value::Map(map) => {
198                        current = map
199                            .entry(component.to_string())
200                            .or_insert_with(|| Value::Map(BTreeMap::new()));
201                    }
202                    Value::Array(arr) => {
203                        let index: usize = component.parse().map_err(|_| {
204                            Error::Path(PathError::InvalidPath {
205                                message: format!("invalid array index: {}", component),
206                            })
207                        })?;
208                        current = arr.get_mut(index).ok_or_else(|| {
209                            Error::Path(PathError::InvalidPath {
210                                message: format!("array index {} out of bounds", index),
211                            })
212                        })?;
213                    }
214                    _ => {
215                        return Err(Error::Path(PathError::InvalidPath {
216                            message: format!(
217                                "cannot navigate through non-container at '{}'",
218                                component
219                            ),
220                        }));
221                    }
222                }
223            }
224        }
225
226        Ok(())
227    }
228
229    /// Remove a value at a path, returning it if it existed.
230    pub fn remove(&mut self, path: &Path) -> Result<Option<Value>, Error> {
231        if path.is_empty() {
232            let old = std::mem::replace(self, Value::Null);
233            return Ok(Some(old));
234        }
235
236        // Navigate to parent
237        let parent_path = path.slice(0, path.len() - 1);
238        let last_component = &path[path.len() - 1];
239
240        let parent = match self.get_mut(&parent_path) {
241            Some(p) => p,
242            None => return Ok(None),
243        };
244
245        match parent {
246            Value::Map(map) => Ok(map.remove(last_component)),
247            Value::Array(arr) => {
248                let index: usize = last_component.parse().map_err(|_| {
249                    Error::Path(PathError::InvalidPath {
250                        message: format!("invalid array index: {}", last_component),
251                    })
252                })?;
253                if index < arr.len() {
254                    Ok(Some(arr.remove(index)))
255                } else {
256                    Ok(None)
257                }
258            }
259            _ => Ok(None),
260        }
261    }
262}
263
264// Conversion from common types
265
266impl From<bool> for Value {
267    fn from(v: bool) -> Self {
268        Value::Bool(v)
269    }
270}
271
272impl From<i64> for Value {
273    fn from(v: i64) -> Self {
274        Value::Integer(v)
275    }
276}
277
278impl From<i32> for Value {
279    fn from(v: i32) -> Self {
280        Value::Integer(v as i64)
281    }
282}
283
284impl From<f64> for Value {
285    fn from(v: f64) -> Self {
286        Value::Float(if v.is_nan() {
287            f64::from_bits(0x7ff8_0000_0000_0000)
288        } else {
289            v
290        })
291    }
292}
293
294impl From<u64> for Value {
295    fn from(v: u64) -> Self {
296        if v <= i64::MAX as u64 {
297            Self::Integer(v as i64)
298        } else {
299            Self::Unsigned(v)
300        }
301    }
302}
303
304impl From<String> for Value {
305    fn from(v: String) -> Self {
306        Value::String(v)
307    }
308}
309
310impl From<&str> for Value {
311    fn from(v: &str) -> Self {
312        Value::String(v.to_string())
313    }
314}
315
316impl From<Vec<u8>> for Value {
317    fn from(v: Vec<u8>) -> Self {
318        Value::Bytes(v)
319    }
320}
321
322impl<T: Into<Value>> From<Vec<T>> for Value {
323    fn from(v: Vec<T>) -> Self {
324        Value::Array(v.into_iter().map(Into::into).collect())
325    }
326}
327
328#[cfg(test)]
329mod tests {
330    use super::*;
331    use crate::path;
332
333    #[test]
334    fn get_nested_value() {
335        let mut value = Value::map();
336        value.set(&path!("foo/bar"), Value::from("hello")).unwrap();
337
338        assert_eq!(value.get(&path!("foo/bar")), Some(&Value::from("hello")));
339        // foo should contain bar, not be empty
340        let foo = value.get(&path!("foo")).unwrap();
341        assert!(foo.is_map());
342        assert_eq!(foo.get(&path!("bar")), Some(&Value::from("hello")));
343        assert_eq!(value.get(&path!("nonexistent")), None);
344    }
345
346    #[test]
347    fn set_creates_intermediate_maps() {
348        let mut value = Value::map();
349        value.set(&path!("a/b/c/d"), Value::from(42i64)).unwrap();
350
351        assert_eq!(value.get(&path!("a/b/c/d")), Some(&Value::from(42i64)));
352        assert!(value.get(&path!("a")).unwrap().is_map());
353        assert!(value.get(&path!("a/b")).unwrap().is_map());
354    }
355
356    #[test]
357    fn remove_works() {
358        let mut value = Value::map();
359        value.set(&path!("foo/bar"), Value::from("hello")).unwrap();
360
361        let removed = value.remove(&path!("foo/bar")).unwrap();
362        assert_eq!(removed, Some(Value::from("hello")));
363        assert_eq!(value.get(&path!("foo/bar")), None);
364
365        // Parent still exists
366        assert!(value.get(&path!("foo")).is_some());
367    }
368
369    #[test]
370    fn array_access_works() {
371        let mut value = Value::map();
372        value
373            .set(
374                &path!("items"),
375                Value::Array(vec![Value::from("a"), Value::from("b"), Value::from("c")]),
376            )
377            .unwrap();
378
379        assert_eq!(value.get(&path!("items/0")), Some(&Value::from("a")));
380        assert_eq!(value.get(&path!("items/1")), Some(&Value::from("b")));
381        assert_eq!(value.get(&path!("items/2")), Some(&Value::from("c")));
382        assert_eq!(value.get(&path!("items/3")), None);
383    }
384
385    #[test]
386    fn value_constructors() {
387        assert!(Value::null().is_null());
388        assert!(Value::map().is_map());
389        assert!(Value::array().is_array());
390    }
391
392    #[test]
393    fn value_default_is_null() {
394        let value = Value::default();
395        assert!(value.is_null());
396    }
397
398    #[test]
399    fn value_type_checks() {
400        assert!(Value::Null.is_null());
401        assert!(!Value::Null.is_map());
402        assert!(!Value::Null.is_array());
403
404        assert!(!Value::Bool(true).is_null());
405        assert!(!Value::Bool(true).is_map());
406        assert!(!Value::Bool(true).is_array());
407
408        assert!(Value::Map(BTreeMap::new()).is_map());
409        assert!(!Value::Map(BTreeMap::new()).is_null());
410
411        assert!(Value::Array(vec![]).is_array());
412        assert!(!Value::Array(vec![]).is_null());
413    }
414
415    #[test]
416    fn get_on_empty_path_returns_self() {
417        let value = Value::from("hello");
418        assert_eq!(value.get(&path!("")), Some(&value));
419    }
420
421    #[test]
422    fn get_on_primitive_returns_none() {
423        let value = Value::from("hello");
424        assert_eq!(value.get(&path!("foo")), None);
425    }
426
427    #[test]
428    fn get_mut_works() {
429        let mut value = Value::map();
430        value.set(&path!("foo"), Value::from("bar")).unwrap();
431
432        let foo = value.get_mut(&path!("foo")).unwrap();
433        *foo = Value::from("baz");
434
435        assert_eq!(value.get(&path!("foo")), Some(&Value::from("baz")));
436    }
437
438    #[test]
439    fn get_mut_on_array() {
440        let mut value = Value::Array(vec![Value::from(1i64), Value::from(2i64)]);
441
442        let first = value.get_mut(&path!("0")).unwrap();
443        *first = Value::from(100i64);
444
445        assert_eq!(value.get(&path!("0")), Some(&Value::from(100i64)));
446    }
447
448    #[test]
449    fn get_mut_on_primitive_returns_none() {
450        let mut value = Value::from("hello");
451        assert!(value.get_mut(&path!("foo")).is_none());
452    }
453
454    #[test]
455    fn set_at_empty_path_replaces_self() {
456        let mut value = Value::from("old");
457        value.set(&path!(""), Value::from("new")).unwrap();
458        assert_eq!(value, Value::from("new"));
459    }
460
461    #[test]
462    fn set_array_element() {
463        let mut value = Value::Array(vec![Value::from("a"), Value::from("b")]);
464        value.set(&path!("0"), Value::from("x")).unwrap();
465        assert_eq!(value.get(&path!("0")), Some(&Value::from("x")));
466    }
467
468    #[test]
469    fn set_array_append() {
470        let mut value = Value::Array(vec![Value::from("a")]);
471        value.set(&path!("1"), Value::from("b")).unwrap();
472        assert_eq!(value.get(&path!("1")), Some(&Value::from("b")));
473    }
474
475    #[test]
476    fn set_array_out_of_bounds_error() {
477        let mut value = Value::Array(vec![Value::from("a")]);
478        let result = value.set(&path!("5"), Value::from("x"));
479        assert!(result.is_err());
480    }
481
482    #[test]
483    fn set_on_primitive_error() {
484        let mut value = Value::from("hello");
485        let result = value.set(&path!("foo"), Value::from("bar"));
486        assert!(result.is_err());
487    }
488
489    #[test]
490    fn set_invalid_array_index_error() {
491        let mut value = Value::Array(vec![Value::from("a")]);
492        let result = value.set(&path!("not_a_number"), Value::from("x"));
493        assert!(result.is_err());
494    }
495
496    #[test]
497    fn set_through_array() {
498        let mut value = Value::map();
499        value
500            .set(
501                &path!("items"),
502                Value::Array(vec![Value::map(), Value::map()]),
503            )
504            .unwrap();
505        value
506            .set(&path!("items/0/name"), Value::from("first"))
507            .unwrap();
508
509        assert_eq!(
510            value.get(&path!("items/0/name")),
511            Some(&Value::from("first"))
512        );
513    }
514
515    #[test]
516    fn set_through_array_invalid_index_error() {
517        let mut value = Value::map();
518        value.set(&path!("items"), Value::Array(vec![])).unwrap();
519        let result = value.set(&path!("items/0/name"), Value::from("x"));
520        assert!(result.is_err());
521    }
522
523    #[test]
524    fn set_through_primitive_error() {
525        let mut value = Value::map();
526        value.set(&path!("foo"), Value::from("primitive")).unwrap();
527        let result = value.set(&path!("foo/bar"), Value::from("x"));
528        assert!(result.is_err());
529    }
530
531    #[test]
532    fn remove_at_empty_path() {
533        let mut value = Value::from("hello");
534        let removed = value.remove(&path!("")).unwrap();
535        assert_eq!(removed, Some(Value::from("hello")));
536        assert!(value.is_null());
537    }
538
539    #[test]
540    fn remove_nonexistent() {
541        let mut value = Value::map();
542        let removed = value.remove(&path!("nonexistent")).unwrap();
543        assert_eq!(removed, None);
544    }
545
546    #[test]
547    fn remove_from_array() {
548        let mut value = Value::Array(vec![Value::from("a"), Value::from("b"), Value::from("c")]);
549        let removed = value.remove(&path!("1")).unwrap();
550        assert_eq!(removed, Some(Value::from("b")));
551
552        // Array should now be [a, c]
553        match &value {
554            Value::Array(arr) => assert_eq!(arr.len(), 2),
555            _ => panic!("Expected array"),
556        }
557    }
558
559    #[test]
560    fn remove_from_array_out_of_bounds() {
561        let mut value = Value::Array(vec![Value::from("a")]);
562        let removed = value.remove(&path!("5")).unwrap();
563        assert_eq!(removed, None);
564    }
565
566    #[test]
567    fn remove_invalid_array_index_error() {
568        let mut value = Value::Array(vec![Value::from("a")]);
569        let result = value.remove(&path!("not_a_number"));
570        assert!(result.is_err());
571    }
572
573    #[test]
574    fn remove_from_primitive() {
575        let mut value = Value::map();
576        value.set(&path!("foo"), Value::from("primitive")).unwrap();
577        let removed = value.remove(&path!("foo/bar")).unwrap();
578        assert_eq!(removed, None);
579    }
580
581    #[test]
582    fn from_bool() {
583        assert_eq!(Value::from(true), Value::Bool(true));
584        assert_eq!(Value::from(false), Value::Bool(false));
585    }
586
587    #[test]
588    fn from_i64() {
589        assert_eq!(Value::from(42i64), Value::Integer(42));
590        assert_eq!(Value::from(-100i64), Value::Integer(-100));
591    }
592
593    #[test]
594    fn from_i32() {
595        assert_eq!(Value::from(42i32), Value::Integer(42));
596    }
597
598    #[test]
599    fn from_f64() {
600        assert_eq!(Value::from(2.75f64), Value::Float(2.75));
601    }
602
603    #[test]
604    fn from_string() {
605        assert_eq!(
606            Value::from("hello".to_string()),
607            Value::String("hello".to_string())
608        );
609    }
610
611    #[test]
612    fn from_str() {
613        assert_eq!(Value::from("hello"), Value::String("hello".to_string()));
614    }
615
616    #[test]
617    fn from_vec_u8() {
618        assert_eq!(Value::from(vec![1u8, 2, 3]), Value::Bytes(vec![1u8, 2, 3]));
619    }
620
621    #[test]
622    fn from_vec_values() {
623        let values: Vec<i64> = vec![1, 2, 3];
624        let value = Value::from(values);
625        match value {
626            Value::Array(arr) => {
627                assert_eq!(arr.len(), 3);
628                assert_eq!(arr[0], Value::Integer(1));
629            }
630            _ => panic!("Expected array"),
631        }
632    }
633
634    #[test]
635    fn value_equality() {
636        assert_eq!(Value::Null, Value::Null);
637        assert_eq!(Value::Bool(true), Value::Bool(true));
638        assert_ne!(Value::Bool(true), Value::Bool(false));
639        assert_eq!(Value::Integer(42), Value::Integer(42));
640        assert_ne!(Value::Integer(42), Value::Integer(43));
641        assert_eq!(
642            Value::String("a".to_string()),
643            Value::String("a".to_string())
644        );
645        assert_ne!(
646            Value::String("a".to_string()),
647            Value::String("b".to_string())
648        );
649    }
650
651    #[test]
652    fn value_clone() {
653        let original = Value::Map({
654            let mut m = BTreeMap::new();
655            m.insert("key".to_string(), Value::from("value"));
656            m
657        });
658        let cloned = original.clone();
659        assert_eq!(original, cloned);
660    }
661
662    #[test]
663    fn value_debug() {
664        let value = Value::from("test");
665        let debug = format!("{:?}", value);
666        assert!(debug.contains("String"));
667        assert!(debug.contains("test"));
668    }
669}