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}