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