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 get_field(&self, col_pos: usize, name: Option<&'static str>) -> Result<&AlgebraicValue, InvalidFieldError> {
76 self.elements.get(col_pos).ok_or(InvalidFieldError {
77 col_pos: col_pos.into(),
78 name,
79 })
80 }
81
82 pub fn project(&self, cols: &ColList) -> Result<AlgebraicValue, InvalidFieldError> {
93 if let Some(head) = cols.as_singleton() {
94 self.get_field(head.idx(), None).cloned()
95 } else {
96 let mut fields = Vec::with_capacity(cols.len() as usize);
97 for col in cols.iter() {
98 fields.push(self.get_field(col.idx(), None)?.clone());
99 }
100 Ok(AlgebraicValue::product(fields))
101 }
102 }
103
104 pub fn project_product(&self, cols: &ColList) -> Result<ProductValue, InvalidFieldError> {
114 let mut fields = Vec::with_capacity(cols.len() as usize);
115 for col in cols.iter() {
116 fields.push(self.get_field(col.idx(), None)?.clone());
117 }
118 Ok(ProductValue::from(fields))
119 }
120
121 pub fn extract_field<'a, T>(
124 &'a self,
125 col_pos: usize,
126 name: Option<&'static str>,
127 f: impl 'a + Fn(&'a AlgebraicValue) -> Option<T>,
128 ) -> Result<T, InvalidFieldError> {
129 f(self.get_field(col_pos, name)?).ok_or(InvalidFieldError {
130 col_pos: col_pos.into(),
131 name,
132 })
133 }
134
135 pub fn field_as_bool(&self, index: usize, named: Option<&'static str>) -> Result<bool, InvalidFieldError> {
137 self.extract_field(index, named, |f| f.as_bool().copied())
138 }
139
140 pub fn field_as_u8(&self, index: usize, named: Option<&'static str>) -> Result<u8, InvalidFieldError> {
142 self.extract_field(index, named, |f| f.as_u8().copied())
143 }
144
145 pub fn field_as_u32(&self, index: usize, named: Option<&'static str>) -> Result<u32, InvalidFieldError> {
147 self.extract_field(index, named, |f| f.as_u32().copied())
148 }
149
150 pub fn field_as_u64(&self, index: usize, named: Option<&'static str>) -> Result<u64, InvalidFieldError> {
152 self.extract_field(index, named, |f| f.as_u64().copied())
153 }
154
155 pub fn field_as_i64(&self, index: usize, named: Option<&'static str>) -> Result<i64, InvalidFieldError> {
157 self.extract_field(index, named, |f| f.as_i64().copied())
158 }
159
160 pub fn field_as_i128(&self, index: usize, named: Option<&'static str>) -> Result<i128, InvalidFieldError> {
162 self.extract_field(index, named, |f| f.as_i128().copied().map(|x| x.0))
163 }
164
165 pub fn field_as_u128(&self, index: usize, named: Option<&'static str>) -> Result<u128, InvalidFieldError> {
167 self.extract_field(index, named, |f| f.as_u128().copied().map(|x| x.0))
168 }
169
170 pub fn field_as_str(&self, index: usize, named: Option<&'static str>) -> Result<&str, InvalidFieldError> {
172 self.extract_field(index, named, |f| f.as_string()).map(|x| &**x)
173 }
174
175 pub fn field_as_bytes(&self, index: usize, named: Option<&'static str>) -> Result<&[u8], InvalidFieldError> {
177 self.extract_field(index, named, |f| f.as_bytes())
178 }
179
180 pub fn field_as_array(&self, index: usize, named: Option<&'static str>) -> Result<&ArrayValue, InvalidFieldError> {
182 self.extract_field(index, named, |f| f.as_array())
183 }
184
185 pub fn field_as_sum(&self, index: usize, named: Option<&'static str>) -> Result<SumValue, InvalidFieldError> {
187 self.extract_field(index, named, |f| f.as_sum().cloned())
188 }
189}
190
191impl<'a> ValueWithType<'a, ProductValue> {
192 pub fn elements(&self) -> impl ExactSizeIterator<Item = ValueWithType<'a, AlgebraicValue>> {
193 self.ty_s().with_values(self.value())
194 }
195}