Skip to main content

bison_db/
value.rs

1//! The document model: [`Value`] and [`Document`].
2//!
3//! A [`Document`] is an ordered set of named fields — the unit a store holds.
4//! Each field's content is a [`Value`], a small tagged union covering the
5//! JSON-like shapes a schemaless record needs: null, boolean, signed integer,
6//! float, UTF-8 string, raw bytes, array, and nested document.
7//!
8//! Fields keep insertion order. Two documents are equal when they carry the
9//! same fields in the same order, so encode/decode round-trips compare equal.
10//! Order is preserved because it is cheap to maintain over a flat vector and
11//! because a stable field order keeps the on-disk encoding deterministic.
12
13use alloc::string::String;
14use alloc::vec::Vec;
15
16/// A single field value inside a [`Document`].
17///
18/// `Value` is deliberately small and closed: it models the shapes a schemaless
19/// document store needs and nothing more. Nesting is expressed through
20/// [`Value::Array`] and [`Value::Object`], so an arbitrarily deep record is
21/// just a tree of `Value`s.
22///
23/// # Examples
24///
25/// ```
26/// use bison_db::Value;
27///
28/// let v = Value::from("hello");
29/// assert_eq!(v.as_str(), Some("hello"));
30/// assert!(Value::from(42_i64).as_int() == Some(42));
31/// assert!(Value::Null.is_null());
32/// ```
33#[derive(Clone, Debug, Default, PartialEq)]
34pub enum Value {
35    /// The absence of a value.
36    #[default]
37    Null,
38    /// A boolean.
39    Bool(bool),
40    /// A signed 64-bit integer. All integral fields are stored at this width.
41    Int(i64),
42    /// A 64-bit IEEE-754 float.
43    Float(f64),
44    /// A UTF-8 string.
45    Str(String),
46    /// An opaque byte string, for binary fields that are not valid UTF-8.
47    Bytes(Vec<u8>),
48    /// An ordered list of values.
49    Array(Vec<Value>),
50    /// A nested document.
51    Object(Document),
52}
53
54impl Value {
55    /// Returns `true` if this value is [`Value::Null`].
56    ///
57    /// # Examples
58    ///
59    /// ```
60    /// use bison_db::Value;
61    /// assert!(Value::Null.is_null());
62    /// assert!(!Value::from(0_i64).is_null());
63    /// ```
64    #[inline]
65    #[must_use]
66    pub const fn is_null(&self) -> bool {
67        matches!(self, Value::Null)
68    }
69
70    /// Returns the boolean if this is a [`Value::Bool`], otherwise `None`.
71    ///
72    /// # Examples
73    ///
74    /// ```
75    /// use bison_db::Value;
76    /// assert_eq!(Value::from(true).as_bool(), Some(true));
77    /// assert_eq!(Value::from(1_i64).as_bool(), None);
78    /// ```
79    #[inline]
80    #[must_use]
81    pub const fn as_bool(&self) -> Option<bool> {
82        match self {
83            Value::Bool(b) => Some(*b),
84            _ => None,
85        }
86    }
87
88    /// Returns the integer if this is a [`Value::Int`], otherwise `None`.
89    ///
90    /// Floats are not coerced; a [`Value::Float`] returns `None`.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use bison_db::Value;
96    /// assert_eq!(Value::from(7_i64).as_int(), Some(7));
97    /// assert_eq!(Value::from(7.0_f64).as_int(), None);
98    /// ```
99    #[inline]
100    #[must_use]
101    pub const fn as_int(&self) -> Option<i64> {
102        match self {
103            Value::Int(n) => Some(*n),
104            _ => None,
105        }
106    }
107
108    /// Returns the float if this is a [`Value::Float`], otherwise `None`.
109    ///
110    /// # Examples
111    ///
112    /// ```
113    /// use bison_db::Value;
114    /// assert_eq!(Value::from(1.5_f64).as_float(), Some(1.5));
115    /// assert_eq!(Value::from(1_i64).as_float(), None);
116    /// ```
117    #[inline]
118    #[must_use]
119    pub const fn as_float(&self) -> Option<f64> {
120        match self {
121            Value::Float(f) => Some(*f),
122            _ => None,
123        }
124    }
125
126    /// Returns the string slice if this is a [`Value::Str`], otherwise `None`.
127    ///
128    /// # Examples
129    ///
130    /// ```
131    /// use bison_db::Value;
132    /// assert_eq!(Value::from("bison").as_str(), Some("bison"));
133    /// ```
134    #[inline]
135    #[must_use]
136    pub fn as_str(&self) -> Option<&str> {
137        match self {
138            Value::Str(s) => Some(s.as_str()),
139            _ => None,
140        }
141    }
142
143    /// Returns the byte slice if this is a [`Value::Bytes`], otherwise `None`.
144    ///
145    /// # Examples
146    ///
147    /// ```
148    /// use bison_db::Value;
149    /// assert_eq!(Value::Bytes(vec![1, 2, 3]).as_bytes(), Some(&[1, 2, 3][..]));
150    /// ```
151    #[inline]
152    #[must_use]
153    pub fn as_bytes(&self) -> Option<&[u8]> {
154        match self {
155            Value::Bytes(b) => Some(b.as_slice()),
156            _ => None,
157        }
158    }
159
160    /// Returns the element slice if this is a [`Value::Array`], otherwise `None`.
161    ///
162    /// # Examples
163    ///
164    /// ```
165    /// use bison_db::Value;
166    /// let v = Value::Array(vec![Value::from(1_i64), Value::from(2_i64)]);
167    /// assert_eq!(v.as_array().map(<[_]>::len), Some(2));
168    /// ```
169    #[inline]
170    #[must_use]
171    pub fn as_array(&self) -> Option<&[Value]> {
172        match self {
173            Value::Array(a) => Some(a.as_slice()),
174            _ => None,
175        }
176    }
177
178    /// Returns the nested document if this is a [`Value::Object`], otherwise `None`.
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// use bison_db::{Document, Value};
184    /// let mut inner = Document::new();
185    /// inner.set("k", 1_i64);
186    /// let v = Value::Object(inner);
187    /// assert_eq!(v.as_object().and_then(|d| d.get("k")).and_then(Value::as_int), Some(1));
188    /// ```
189    #[inline]
190    #[must_use]
191    pub fn as_object(&self) -> Option<&Document> {
192        match self {
193            Value::Object(d) => Some(d),
194            _ => None,
195        }
196    }
197
198    /// Returns a short, stable name for the value's variant.
199    ///
200    /// Intended for diagnostics and error messages, not for logic; match on the
201    /// variant directly when behaviour depends on the type.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// use bison_db::Value;
207    /// assert_eq!(Value::from(1_i64).type_name(), "int");
208    /// assert_eq!(Value::Null.type_name(), "null");
209    /// ```
210    #[must_use]
211    pub const fn type_name(&self) -> &'static str {
212        match self {
213            Value::Null => "null",
214            Value::Bool(_) => "bool",
215            Value::Int(_) => "int",
216            Value::Float(_) => "float",
217            Value::Str(_) => "string",
218            Value::Bytes(_) => "bytes",
219            Value::Array(_) => "array",
220            Value::Object(_) => "object",
221        }
222    }
223}
224
225impl From<bool> for Value {
226    #[inline]
227    fn from(v: bool) -> Self {
228        Value::Bool(v)
229    }
230}
231
232impl From<i32> for Value {
233    #[inline]
234    fn from(v: i32) -> Self {
235        Value::Int(i64::from(v))
236    }
237}
238
239impl From<i64> for Value {
240    #[inline]
241    fn from(v: i64) -> Self {
242        Value::Int(v)
243    }
244}
245
246impl From<u32> for Value {
247    #[inline]
248    fn from(v: u32) -> Self {
249        Value::Int(i64::from(v))
250    }
251}
252
253impl From<f64> for Value {
254    #[inline]
255    fn from(v: f64) -> Self {
256        Value::Float(v)
257    }
258}
259
260impl From<&str> for Value {
261    #[inline]
262    fn from(v: &str) -> Self {
263        Value::Str(String::from(v))
264    }
265}
266
267impl From<String> for Value {
268    #[inline]
269    fn from(v: String) -> Self {
270        Value::Str(v)
271    }
272}
273
274impl From<Vec<u8>> for Value {
275    #[inline]
276    fn from(v: Vec<u8>) -> Self {
277        Value::Bytes(v)
278    }
279}
280
281impl From<Vec<Value>> for Value {
282    #[inline]
283    fn from(v: Vec<Value>) -> Self {
284        Value::Array(v)
285    }
286}
287
288impl From<Document> for Value {
289    #[inline]
290    fn from(v: Document) -> Self {
291        Value::Object(v)
292    }
293}
294
295impl<T: Into<Value>> From<Option<T>> for Value {
296    /// `Some(x)` becomes `x`'s value; `None` becomes [`Value::Null`].
297    #[inline]
298    fn from(v: Option<T>) -> Self {
299        match v {
300            Some(inner) => inner.into(),
301            None => Value::Null,
302        }
303    }
304}
305
306/// An ordered collection of named fields — the record a store holds.
307///
308/// A document maps `String` keys to [`Value`]s and preserves the order in which
309/// fields were first inserted. Lookups are a linear scan, which is the fastest
310/// strategy for the small field counts typical of documents: it keeps the keys
311/// contiguous in memory and avoids the hashing and pointer-chasing overhead a
312/// map would add at this size.
313///
314/// # Examples
315///
316/// ```
317/// use bison_db::Document;
318///
319/// let mut user = Document::new();
320/// user.set("name", "ada").set("born", 1815_i64);
321///
322/// assert_eq!(user.len(), 2);
323/// assert_eq!(user.get("name").and_then(|v| v.as_str()), Some("ada"));
324/// ```
325#[derive(Clone, Debug, Default, PartialEq)]
326pub struct Document {
327    fields: Vec<(String, Value)>,
328}
329
330impl Document {
331    /// Creates an empty document.
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// use bison_db::Document;
337    /// let doc = Document::new();
338    /// assert!(doc.is_empty());
339    /// ```
340    #[inline]
341    #[must_use]
342    pub const fn new() -> Self {
343        Document { fields: Vec::new() }
344    }
345
346    /// Creates an empty document with room for `capacity` fields before it
347    /// needs to reallocate.
348    ///
349    /// Use this when the field count is known up front to avoid intermediate
350    /// growth allocations.
351    ///
352    /// # Examples
353    ///
354    /// ```
355    /// use bison_db::Document;
356    /// let doc = Document::with_capacity(4);
357    /// assert!(doc.is_empty());
358    /// ```
359    #[inline]
360    #[must_use]
361    pub fn with_capacity(capacity: usize) -> Self {
362        Document {
363            fields: Vec::with_capacity(capacity),
364        }
365    }
366
367    /// Sets `key` to `value`, returning `&mut self` so calls can be chained.
368    ///
369    /// If `key` is already present its value is replaced in place, preserving
370    /// the field's original position. Otherwise the field is appended.
371    ///
372    /// # Examples
373    ///
374    /// ```
375    /// use bison_db::Document;
376    /// let mut doc = Document::new();
377    /// doc.set("a", 1_i64).set("b", 2_i64).set("a", 3_i64);
378    /// // "a" keeps its leading position but takes the new value.
379    /// assert_eq!(doc.keys().collect::<Vec<_>>(), ["a", "b"]);
380    /// assert_eq!(doc.get("a").and_then(|v| v.as_int()), Some(3));
381    /// ```
382    pub fn set<K, V>(&mut self, key: K, value: V) -> &mut Self
383    where
384        K: Into<String>,
385        V: Into<Value>,
386    {
387        let key = key.into();
388        let value = value.into();
389        match self.fields.iter_mut().find(|(k, _)| *k == key) {
390            Some(slot) => slot.1 = value,
391            None => self.fields.push((key, value)),
392        }
393        self
394    }
395
396    /// Returns a reference to the value for `key`, or `None` if absent.
397    ///
398    /// # Examples
399    ///
400    /// ```
401    /// use bison_db::Document;
402    /// let mut doc = Document::new();
403    /// doc.set("k", "v");
404    /// assert_eq!(doc.get("k").and_then(|v| v.as_str()), Some("v"));
405    /// assert!(doc.get("missing").is_none());
406    /// ```
407    #[must_use]
408    pub fn get(&self, key: &str) -> Option<&Value> {
409        self.fields.iter().find(|(k, _)| k == key).map(|(_, v)| v)
410    }
411
412    /// Returns a mutable reference to the value for `key`, or `None` if absent.
413    ///
414    /// # Examples
415    ///
416    /// ```
417    /// use bison_db::{Document, Value};
418    /// let mut doc = Document::new();
419    /// doc.set("n", 1_i64);
420    /// if let Some(Value::Int(n)) = doc.get_mut("n") {
421    ///     *n += 41;
422    /// }
423    /// assert_eq!(doc.get("n").and_then(|v| v.as_int()), Some(42));
424    /// ```
425    #[must_use]
426    pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
427        self.fields
428            .iter_mut()
429            .find(|(k, _)| k == key)
430            .map(|(_, v)| v)
431    }
432
433    /// Returns `true` if `key` is present.
434    ///
435    /// # Examples
436    ///
437    /// ```
438    /// use bison_db::Document;
439    /// let mut doc = Document::new();
440    /// doc.set("k", 1_i64);
441    /// assert!(doc.contains_key("k"));
442    /// assert!(!doc.contains_key("other"));
443    /// ```
444    #[must_use]
445    pub fn contains_key(&self, key: &str) -> bool {
446        self.fields.iter().any(|(k, _)| k == key)
447    }
448
449    /// Removes `key`, returning its value if it was present.
450    ///
451    /// Remaining fields keep their relative order.
452    ///
453    /// # Examples
454    ///
455    /// ```
456    /// use bison_db::Document;
457    /// let mut doc = Document::new();
458    /// doc.set("a", 1_i64).set("b", 2_i64);
459    /// assert_eq!(doc.remove("a").and_then(|v| v.as_int()), Some(1));
460    /// assert!(!doc.contains_key("a"));
461    /// assert_eq!(doc.keys().collect::<Vec<_>>(), ["b"]);
462    /// ```
463    pub fn remove(&mut self, key: &str) -> Option<Value> {
464        let idx = self.fields.iter().position(|(k, _)| k == key)?;
465        Some(self.fields.remove(idx).1)
466    }
467
468    /// Returns the number of fields.
469    ///
470    /// # Examples
471    ///
472    /// ```
473    /// use bison_db::Document;
474    /// let mut doc = Document::new();
475    /// doc.set("a", 1_i64);
476    /// assert_eq!(doc.len(), 1);
477    /// ```
478    #[inline]
479    #[must_use]
480    pub fn len(&self) -> usize {
481        self.fields.len()
482    }
483
484    /// Returns `true` if the document has no fields.
485    ///
486    /// # Examples
487    ///
488    /// ```
489    /// use bison_db::Document;
490    /// assert!(Document::new().is_empty());
491    /// ```
492    #[inline]
493    #[must_use]
494    pub fn is_empty(&self) -> bool {
495        self.fields.is_empty()
496    }
497
498    /// Removes all fields, keeping the allocated capacity for reuse.
499    ///
500    /// # Examples
501    ///
502    /// ```
503    /// use bison_db::Document;
504    /// let mut doc = Document::new();
505    /// doc.set("a", 1_i64);
506    /// doc.clear();
507    /// assert!(doc.is_empty());
508    /// ```
509    #[inline]
510    pub fn clear(&mut self) {
511        self.fields.clear();
512    }
513
514    /// Returns an iterator over the fields as `(&str, &Value)` pairs, in order.
515    ///
516    /// # Examples
517    ///
518    /// ```
519    /// use bison_db::Document;
520    /// let mut doc = Document::new();
521    /// doc.set("a", 1_i64).set("b", 2_i64);
522    /// let collected: Vec<_> = doc.iter().map(|(k, _)| k).collect();
523    /// assert_eq!(collected, ["a", "b"]);
524    /// ```
525    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
526        self.fields.iter().map(|(k, v)| (k.as_str(), v))
527    }
528
529    /// Returns an iterator over the field keys, in order.
530    ///
531    /// # Examples
532    ///
533    /// ```
534    /// use bison_db::Document;
535    /// let mut doc = Document::new();
536    /// doc.set("x", 1_i64);
537    /// assert_eq!(doc.keys().collect::<Vec<_>>(), ["x"]);
538    /// ```
539    pub fn keys(&self) -> impl Iterator<Item = &str> {
540        self.fields.iter().map(|(k, _)| k.as_str())
541    }
542
543    /// Returns an iterator over the field values, in order.
544    ///
545    /// # Examples
546    ///
547    /// ```
548    /// use bison_db::Document;
549    /// let mut doc = Document::new();
550    /// doc.set("x", 9_i64);
551    /// assert_eq!(doc.values().filter_map(|v| v.as_int()).sum::<i64>(), 9);
552    /// ```
553    pub fn values(&self) -> impl Iterator<Item = &Value> {
554        self.fields.iter().map(|(_, v)| v)
555    }
556}
557
558impl<'a> IntoIterator for &'a Document {
559    type Item = (&'a str, &'a Value);
560    type IntoIter = core::iter::Map<
561        core::slice::Iter<'a, (String, Value)>,
562        fn(&'a (String, Value)) -> (&'a str, &'a Value),
563    >;
564
565    fn into_iter(self) -> Self::IntoIter {
566        fn pair(kv: &(String, Value)) -> (&str, &Value) {
567            (kv.0.as_str(), &kv.1)
568        }
569        self.fields.iter().map(pair)
570    }
571}
572
573impl<K, V> FromIterator<(K, V)> for Document
574where
575    K: Into<String>,
576    V: Into<Value>,
577{
578    /// Builds a document from key/value pairs. Later duplicates overwrite
579    /// earlier ones, matching [`Document::set`].
580    ///
581    /// # Examples
582    ///
583    /// ```
584    /// use bison_db::{Document, Value};
585    /// let doc: Document = [("a", 1_i64), ("b", 2_i64)].into_iter().collect();
586    /// assert_eq!(doc.len(), 2);
587    /// ```
588    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
589        let iter = iter.into_iter();
590        let mut doc = Document::with_capacity(iter.size_hint().0);
591        for (k, v) in iter {
592            let _ = doc.set(k, v);
593        }
594        doc
595    }
596}
597
598#[cfg(test)]
599mod tests {
600    use super::*;
601
602    #[test]
603    fn test_set_existing_key_replaces_in_place() {
604        let mut doc = Document::new();
605        doc.set("a", 1_i64).set("b", 2_i64).set("a", 9_i64);
606        assert_eq!(doc.keys().collect::<Vec<_>>(), ["a", "b"]);
607        assert_eq!(doc.get("a").and_then(Value::as_int), Some(9));
608    }
609
610    #[test]
611    fn test_remove_absent_key_returns_none() {
612        let mut doc = Document::new();
613        doc.set("a", 1_i64);
614        assert!(doc.remove("missing").is_none());
615        assert_eq!(doc.len(), 1);
616    }
617
618    #[test]
619    fn test_remove_preserves_order() {
620        let mut doc = Document::new();
621        doc.set("a", 1_i64).set("b", 2_i64).set("c", 3_i64);
622        let _ = doc.remove("b");
623        assert_eq!(doc.keys().collect::<Vec<_>>(), ["a", "c"]);
624    }
625
626    #[test]
627    fn test_value_accessors_reject_wrong_variant() {
628        assert!(Value::from(1_i64).as_str().is_none());
629        assert!(Value::from("x").as_int().is_none());
630        assert!(Value::Null.as_bool().is_none());
631    }
632
633    #[test]
634    fn test_from_option_maps_none_to_null() {
635        let none: Option<i64> = None;
636        assert!(Value::from(none).is_null());
637        assert_eq!(Value::from(Some(5_i64)).as_int(), Some(5));
638    }
639
640    #[test]
641    fn test_equality_is_order_sensitive() {
642        let mut a = Document::new();
643        a.set("x", 1_i64).set("y", 2_i64);
644        let mut b = Document::new();
645        b.set("y", 2_i64).set("x", 1_i64);
646        assert_ne!(a, b);
647    }
648}