toasty_core/stmt/sparse_record.rs
1use super::{PathFieldSet, Type, Value, ValueRecord};
2use std::hash::Hash;
3
4/// A record where only a subset of fields are populated.
5///
6/// Unlike [`ValueRecord`] (which stores values for every field by position),
7/// `SparseRecord` tracks which field indices are present via a
8/// [`PathFieldSet`] and stores corresponding values. Fields not in the set
9/// are absent (not merely null).
10///
11/// Iterating over a `SparseRecord` yields `(usize, Value)` pairs of field
12/// index and value.
13///
14/// # Examples
15///
16/// ```
17/// use toasty_core::stmt::{Value, PathFieldSet};
18///
19/// // Create a sparse record with field 0 populated
20/// let v = Value::empty_sparse_record();
21/// assert!(matches!(v, Value::SparseRecord(_)));
22/// ```
23#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24pub struct SparseRecord {
25 /// Bit set of field indices that are populated in this record.
26 pub fields: PathFieldSet,
27
28 /// Values indexed by field position. Indices not in `fields` contain
29 /// placeholder [`Value::Null`] entries.
30 pub values: Vec<Value>,
31}
32
33impl Value {
34 /// Creates an empty [`Value::SparseRecord`] with no populated fields.
35 pub fn empty_sparse_record() -> Self {
36 SparseRecord {
37 fields: PathFieldSet::new(),
38 values: vec![],
39 }
40 .into()
41 }
42
43 /// Creates a [`Value::SparseRecord`] by distributing values from `record`
44 /// into the positions specified by `fields`.
45 pub fn sparse_record(fields: PathFieldSet, record: ValueRecord) -> Self {
46 let mut values = vec![];
47
48 for (index, value) in fields.iter().zip(record.fields.into_iter()) {
49 while index >= values.len() {
50 values.push(Value::Null);
51 }
52
53 values[index] = value;
54 }
55
56 SparseRecord { fields, values }.into()
57 }
58
59 /// Consumes this value and returns the contained [`SparseRecord`],
60 /// panicking if this is not a [`Value::SparseRecord`].
61 ///
62 /// # Panics
63 ///
64 /// Panics if the value is not a `SparseRecord` variant.
65 pub fn into_sparse_record(self) -> SparseRecord {
66 match self {
67 Self::SparseRecord(value) => value,
68 _ => todo!(),
69 }
70 }
71}
72
73impl Type {
74 /// Creates a [`Type::SparseRecord`] type with the given field set.
75 pub fn sparse_record(fields: impl Into<PathFieldSet>) -> Self {
76 Self::SparseRecord(fields.into())
77 }
78
79 /// Creates a [`Type::SparseRecord`] type with no fields.
80 pub fn empty_sparse_record() -> Self {
81 Self::SparseRecord(PathFieldSet::default())
82 }
83}
84
85impl IntoIterator for SparseRecord {
86 type Item = (usize, Value);
87
88 type IntoIter = Box<dyn Iterator<Item = (usize, Value)>>;
89
90 fn into_iter(self) -> Self::IntoIter {
91 Box::new(
92 self.values
93 .into_iter()
94 .enumerate()
95 .filter_map(move |(i, value)| {
96 if self.fields.contains(i) {
97 Some((i, value))
98 } else {
99 None
100 }
101 }),
102 )
103 }
104}
105
106impl From<SparseRecord> for Value {
107 fn from(value: SparseRecord) -> Self {
108 Self::SparseRecord(value)
109 }
110}