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, Default)]
pub struct ProductValue {
pub elements: Box<[AlgebraicValue]>,
}
#[macro_export]
macro_rules! product {
[$($elems:expr),*$(,)?] => {
$crate::ProductValue {
elements: [$($crate::AlgebraicValue::from($elems)),*].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 {
Vec::from(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 From<ColId> for InvalidFieldError {
fn from(col_pos: ColId) -> Self {
Self { col_pos, name: None }
}
}
impl ProductValue {
pub fn push(self, val: impl Into<AlgebraicValue>) -> Self {
let mut vals: Vec<_> = self.elements.into();
vals.reserve(1);
vals.push(val.into());
Self::from(vals)
}
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, cols: &ColList) -> Result<AlgebraicValue, InvalidFieldError> {
if let Some(head) = cols.as_singleton() {
self.get_field(head.idx(), None).cloned()
} else {
let mut fields = Vec::with_capacity(cols.len() as usize);
for col in cols.iter() {
fields.push(self.get_field(col.idx(), None)?.clone());
}
Ok(AlgebraicValue::product(fields))
}
}
pub fn project_product(&self, cols: &ColList) -> Result<ProductValue, InvalidFieldError> {
let mut fields = Vec::with_capacity(cols.len() as usize);
for col in cols.iter() {
fields.push(self.get_field(col.idx(), None)?.clone());
}
Ok(ProductValue::from(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().map(|x| x.0))
}
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().map(|x| x.0))
}
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>> + use<'a> {
self.ty_s().with_values(self.value())
}
}