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> {
92 if let Some(head) = cols.as_singleton() {
93 self.get_field(head.idx(), None).cloned()
94 } else {
95 let mut fields = Vec::with_capacity(cols.len() as usize);
96 for col in cols.iter() {
97 fields.push(self.get_field(col.idx(), None)?.clone());
98 }
99 Ok(AlgebraicValue::product(fields))
100 }
101 }
102
103 pub fn extract_field<'a, T>(
106 &'a self,
107 col_pos: usize,
108 name: Option<&'static str>,
109 f: impl 'a + Fn(&'a AlgebraicValue) -> Option<T>,
110 ) -> Result<T, InvalidFieldError> {
111 f(self.get_field(col_pos, name)?).ok_or(InvalidFieldError {
112 col_pos: col_pos.into(),
113 name,
114 })
115 }
116
117 pub fn field_as_bool(&self, index: usize, named: Option<&'static str>) -> Result<bool, InvalidFieldError> {
119 self.extract_field(index, named, |f| f.as_bool().copied())
120 }
121
122 pub fn field_as_u8(&self, index: usize, named: Option<&'static str>) -> Result<u8, InvalidFieldError> {
124 self.extract_field(index, named, |f| f.as_u8().copied())
125 }
126
127 pub fn field_as_u32(&self, index: usize, named: Option<&'static str>) -> Result<u32, InvalidFieldError> {
129 self.extract_field(index, named, |f| f.as_u32().copied())
130 }
131
132 pub fn field_as_u64(&self, index: usize, named: Option<&'static str>) -> Result<u64, InvalidFieldError> {
134 self.extract_field(index, named, |f| f.as_u64().copied())
135 }
136
137 pub fn field_as_i64(&self, index: usize, named: Option<&'static str>) -> Result<i64, InvalidFieldError> {
139 self.extract_field(index, named, |f| f.as_i64().copied())
140 }
141
142 pub fn field_as_i128(&self, index: usize, named: Option<&'static str>) -> Result<i128, InvalidFieldError> {
144 self.extract_field(index, named, |f| f.as_i128().copied().map(|x| x.0))
145 }
146
147 pub fn field_as_u128(&self, index: usize, named: Option<&'static str>) -> Result<u128, InvalidFieldError> {
149 self.extract_field(index, named, |f| f.as_u128().copied().map(|x| x.0))
150 }
151
152 pub fn field_as_str(&self, index: usize, named: Option<&'static str>) -> Result<&str, InvalidFieldError> {
154 self.extract_field(index, named, |f| f.as_string()).map(|x| &**x)
155 }
156
157 pub fn field_as_bytes(&self, index: usize, named: Option<&'static str>) -> Result<&[u8], InvalidFieldError> {
159 self.extract_field(index, named, |f| f.as_bytes())
160 }
161
162 pub fn field_as_array(&self, index: usize, named: Option<&'static str>) -> Result<&ArrayValue, InvalidFieldError> {
164 self.extract_field(index, named, |f| f.as_array())
165 }
166
167 pub fn field_as_sum(&self, index: usize, named: Option<&'static str>) -> Result<SumValue, InvalidFieldError> {
169 self.extract_field(index, named, |f| f.as_sum().cloned())
170 }
171}
172
173impl<'a> ValueWithType<'a, ProductValue> {
174 pub fn elements(&self) -> impl ExactSizeIterator<Item = ValueWithType<'a, AlgebraicValue>> {
175 self.ty_s().with_values(self.value())
176 }
177}