Skip to main content

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}