use crate::algebraic_value::AlgebraicValue;
use crate::product_type::ProductType;
use crate::{ArrayValue, SumValue, ValueWithType};
use spacetimedb_primitives::{ColId, ColList};
#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq, Hash)]
pub struct ProductValue {
pub elements: Vec<AlgebraicValue>,
}
#[macro_export]
macro_rules! product {
[$($elems:expr),*$(,)?] => {
$crate::ProductValue {
elements: vec![$($crate::AlgebraicValue::from($elems)),*],
}
}
}
impl ProductValue {
pub fn new(elements: &[AlgebraicValue]) -> Self {
Self {
elements: elements.into(),
}
}
}
impl FromIterator<AlgebraicValue> for ProductValue {
fn from_iter<T: IntoIterator<Item = AlgebraicValue>>(iter: T) -> Self {
let elements = iter.into_iter().collect();
Self { elements }
}
}
impl IntoIterator for ProductValue {
type Item = AlgebraicValue;
type IntoIter = std::vec::IntoIter<AlgebraicValue>;
fn into_iter(self) -> Self::IntoIter {
self.elements.into_iter()
}
}
impl<'a> IntoIterator for &'a ProductValue {
type Item = &'a AlgebraicValue;
type IntoIter = std::slice::Iter<'a, AlgebraicValue>;
fn into_iter(self) -> Self::IntoIter {
self.elements.iter()
}
}
impl crate::Value for ProductValue {
type Type = ProductType;
}
#[derive(thiserror::Error, Debug, Copy, Clone)]
#[error("Field at position {col_pos} named: {name:?} not found or has an invalid type")]
pub struct InvalidFieldError {
pub col_pos: ColId,
pub name: Option<&'static str>,
}
impl ProductValue {
pub fn get_field(&self, col_pos: usize, name: Option<&'static str>) -> Result<&AlgebraicValue, InvalidFieldError> {
self.elements.get(col_pos).ok_or(InvalidFieldError {
col_pos: col_pos.into(),
name,
})
}
pub fn project(&self, indexes: &[(ColId, Option<&'static str>)]) -> Result<AlgebraicValue, InvalidFieldError> {
let fields = match indexes {
[(index, name)] => self.get_field((*index).into(), *name)?.clone(),
indexes => {
let fields: Result<Vec<_>, _> = indexes
.iter()
.map(|(index, name)| self.get_field((*index).into(), *name).cloned())
.collect();
AlgebraicValue::Product(ProductValue::new(&fields?))
}
};
Ok(fields)
}
pub fn project_not_empty(&self, cols: &ColList) -> Result<AlgebraicValue, InvalidFieldError> {
let proj_len = cols.len();
if proj_len == 1 {
self.get_field(cols.head().idx(), None).cloned()
} else {
let mut fields = Vec::with_capacity(proj_len as usize);
for col in cols.iter() {
fields.push(self.get_field(col.idx(), None)?.clone());
}
Ok(AlgebraicValue::product(fields))
}
}
pub fn extract_field<'a, T>(
&'a self,
col_pos: usize,
name: Option<&'static str>,
f: impl 'a + Fn(&'a AlgebraicValue) -> Option<T>,
) -> Result<T, InvalidFieldError> {
f(self.get_field(col_pos, name)?).ok_or(InvalidFieldError {
col_pos: col_pos.into(),
name,
})
}
pub fn field_as_bool(&self, index: usize, named: Option<&'static str>) -> Result<bool, InvalidFieldError> {
self.extract_field(index, named, |f| f.as_bool().copied())
}
pub fn field_as_u8(&self, index: usize, named: Option<&'static str>) -> Result<u8, InvalidFieldError> {
self.extract_field(index, named, |f| f.as_u8().copied())
}
pub fn field_as_u32(&self, index: usize, named: Option<&'static str>) -> Result<u32, InvalidFieldError> {
self.extract_field(index, named, |f| f.as_u32().copied())
}
pub fn field_as_u64(&self, index: usize, named: Option<&'static str>) -> Result<u64, InvalidFieldError> {
self.extract_field(index, named, |f| f.as_u64().copied())
}
pub fn field_as_i64(&self, index: usize, named: Option<&'static str>) -> Result<i64, InvalidFieldError> {
self.extract_field(index, named, |f| f.as_i64().copied())
}
pub fn field_as_i128(&self, index: usize, named: Option<&'static str>) -> Result<i128, InvalidFieldError> {
self.extract_field(index, named, |f| f.as_i128().copied())
}
pub fn field_as_u128(&self, index: usize, named: Option<&'static str>) -> Result<u128, InvalidFieldError> {
self.extract_field(index, named, |f| f.as_u128().copied())
}
pub fn field_as_str(&self, index: usize, named: Option<&'static str>) -> Result<&str, InvalidFieldError> {
self.extract_field(index, named, |f| f.as_string()).map(|x| &**x)
}
pub fn field_as_bytes(&self, index: usize, named: Option<&'static str>) -> Result<&[u8], InvalidFieldError> {
self.extract_field(index, named, |f| f.as_bytes())
}
pub fn field_as_array(&self, index: usize, named: Option<&'static str>) -> Result<&ArrayValue, InvalidFieldError> {
self.extract_field(index, named, |f| f.as_array())
}
pub fn field_as_sum(&self, index: usize, named: Option<&'static str>) -> Result<SumValue, InvalidFieldError> {
self.extract_field(index, named, |f| f.as_sum().cloned())
}
}
impl<'a> ValueWithType<'a, ProductValue> {
pub fn elements(&self) -> impl ExactSizeIterator<Item = ValueWithType<'a, AlgebraicValue>> {
self.ty_s().with_values(self.value())
}
}