1use crate::algebraic_value::AlgebraicValue;
2use crate::product_type::ProductType;
3use crate::{ArrayValue, SumValue, ValueWithType};
4use spacetimedb_primitives::{ColId, ColList};
5
6#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq, Default)]
11pub struct ProductValue {
12 pub elements: Box<[AlgebraicValue]>,
14}
15
16#[macro_export]
20macro_rules! product {
21 [$($elems:expr),*$(,)?] => {
22 $crate::ProductValue {
23 elements: [$($crate::AlgebraicValue::from($elems)),*].into(),
24 }
25 }
26}
27
28impl FromIterator<AlgebraicValue> for ProductValue {
29 fn from_iter<T: IntoIterator<Item = AlgebraicValue>>(iter: T) -> Self {
30 let elements = iter.into_iter().collect();
31 Self { elements }
32 }
33}
34
35impl IntoIterator for ProductValue {
36 type Item = AlgebraicValue;
37 type IntoIter = std::vec::IntoIter<AlgebraicValue>;
38 fn into_iter(self) -> Self::IntoIter {
39 Vec::from(self.elements).into_iter()
40 }
41}
42
43impl<'a> IntoIterator for &'a ProductValue {
44 type Item = &'a AlgebraicValue;
45 type IntoIter = std::slice::Iter<'a, AlgebraicValue>;
46 fn into_iter(self) -> Self::IntoIter {
47 self.elements.iter()
48 }
49}
50
51impl crate::Value for ProductValue {
52 type Type = ProductType;
53}
54
55#[derive(thiserror::Error, Debug, Copy, Clone)]
57#[error("Field at position {col_pos} named: {name:?} not found or has an invalid type")]
58pub struct InvalidFieldError {
59 pub col_pos: ColId,
61 pub name: Option<&'static str>,
63}
64
65impl From<ColId> for InvalidFieldError {
66 fn from(col_pos: ColId) -> Self {
67 Self { col_pos, name: None }
68 }
69}
70
71impl ProductValue {
72 pub fn push(self, val: impl Into<AlgebraicValue>) -> Self {
74 let mut vals: Vec<_> = self.elements.into();
75 vals.reserve(1);
76 vals.push(val.into());
77 Self::from(vals)
78 }
79
80 pub fn get_field(&self, col_pos: usize, name: Option<&'static str>) -> Result<&AlgebraicValue, InvalidFieldError> {
84 self.elements.get(col_pos).ok_or(InvalidFieldError {
85 col_pos: col_pos.into(),
86 name,
87 })
88 }
89
90 pub fn project(&self, cols: &ColList) -> Result<AlgebraicValue, InvalidFieldError> {
101 if let Some(head) = cols.as_singleton() {
102 self.get_field(head.idx(), None).cloned()
103 } else {
104 let mut fields = Vec::with_capacity(cols.len() as usize);
105 for col in cols.iter() {
106 fields.push(self.get_field(col.idx(), None)?.clone());
107 }
108 Ok(AlgebraicValue::product(fields))
109 }
110 }
111
112 pub fn project_product(&self, cols: &ColList) -> Result<ProductValue, InvalidFieldError> {
122 let mut fields = Vec::with_capacity(cols.len() as usize);
123 for col in cols.iter() {
124 fields.push(self.get_field(col.idx(), None)?.clone());
125 }
126 Ok(ProductValue::from(fields))
127 }
128
129 pub fn extract_field<'a, T>(
132 &'a self,
133 col_pos: usize,
134 name: Option<&'static str>,
135 f: impl 'a + Fn(&'a AlgebraicValue) -> Option<T>,
136 ) -> Result<T, InvalidFieldError> {
137 f(self.get_field(col_pos, name)?).ok_or(InvalidFieldError {
138 col_pos: col_pos.into(),
139 name,
140 })
141 }
142
143 pub fn field_as_bool(&self, index: usize, named: Option<&'static str>) -> Result<bool, InvalidFieldError> {
145 self.extract_field(index, named, |f| f.as_bool().copied())
146 }
147
148 pub fn field_as_u8(&self, index: usize, named: Option<&'static str>) -> Result<u8, InvalidFieldError> {
150 self.extract_field(index, named, |f| f.as_u8().copied())
151 }
152
153 pub fn field_as_u32(&self, index: usize, named: Option<&'static str>) -> Result<u32, InvalidFieldError> {
155 self.extract_field(index, named, |f| f.as_u32().copied())
156 }
157
158 pub fn field_as_u64(&self, index: usize, named: Option<&'static str>) -> Result<u64, InvalidFieldError> {
160 self.extract_field(index, named, |f| f.as_u64().copied())
161 }
162
163 pub fn field_as_i64(&self, index: usize, named: Option<&'static str>) -> Result<i64, InvalidFieldError> {
165 self.extract_field(index, named, |f| f.as_i64().copied())
166 }
167
168 pub fn field_as_i128(&self, index: usize, named: Option<&'static str>) -> Result<i128, InvalidFieldError> {
170 self.extract_field(index, named, |f| f.as_i128().copied().map(|x| x.0))
171 }
172
173 pub fn field_as_u128(&self, index: usize, named: Option<&'static str>) -> Result<u128, InvalidFieldError> {
175 self.extract_field(index, named, |f| f.as_u128().copied().map(|x| x.0))
176 }
177
178 pub fn field_as_str(&self, index: usize, named: Option<&'static str>) -> Result<&str, InvalidFieldError> {
180 self.extract_field(index, named, |f| f.as_string()).map(|x| &**x)
181 }
182
183 pub fn field_as_bytes(&self, index: usize, named: Option<&'static str>) -> Result<&[u8], InvalidFieldError> {
185 self.extract_field(index, named, |f| f.as_bytes())
186 }
187
188 pub fn field_as_array(&self, index: usize, named: Option<&'static str>) -> Result<&ArrayValue, InvalidFieldError> {
190 self.extract_field(index, named, |f| f.as_array())
191 }
192
193 pub fn field_as_sum(&self, index: usize, named: Option<&'static str>) -> Result<SumValue, InvalidFieldError> {
195 self.extract_field(index, named, |f| f.as_sum().cloned())
196 }
197}
198
199impl<'a> ValueWithType<'a, ProductValue> {
200 pub fn elements(&self) -> impl ExactSizeIterator<Item = ValueWithType<'a, AlgebraicValue>> + use<'a> {
201 self.ty_s().with_values(self.value())
202 }
203}