use crate::has_opcode::HasOpCode;
use crate::serialization::op_code::OpCode;
use crate::serialization::sigma_byte_reader::SigmaByteRead;
use crate::serialization::sigma_byte_writer::SigmaByteWrite;
use crate::serialization::SigmaParsingError;
use crate::serialization::SigmaSerializable;
use crate::serialization::SigmaSerializeResult;
use crate::types::stype::SType;
use super::constant::Constant;
use super::constant::TryExtractFromError;
use super::constant::TryExtractInto;
use super::expr::Expr;
use super::expr::InvalidArgumentError;
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum Collection {
BoolConstants(Vec<bool>),
Exprs {
elem_tpe: SType,
items: Vec<Expr>,
},
}
impl Collection {
pub fn new(elem_tpe: SType, items: Vec<Expr>) -> Result<Self, InvalidArgumentError> {
if !items.iter().all(|i| i.tpe() == elem_tpe) {
return Err(InvalidArgumentError(format!(
"expected items to be of the same type {0:?}, got {1:?}",
elem_tpe, items
)));
}
if elem_tpe == SType::SBoolean {
let maybe_bools: Result<Vec<bool>, TryExtractFromError> = items
.clone()
.into_iter()
.map(|i| i.try_extract_into::<Constant>()?.try_extract_into::<bool>())
.collect();
match maybe_bools {
Ok(bools) => Ok(Collection::BoolConstants(bools)),
Err(_) => Ok(Collection::Exprs { elem_tpe, items }),
}
} else {
Ok(Collection::Exprs { elem_tpe, items })
}
}
pub fn tpe(&self) -> SType {
SType::SColl(
match self {
Collection::BoolConstants(_) => SType::SBoolean,
Collection::Exprs { elem_tpe, items: _ } => elem_tpe.clone(),
}
.into(),
)
}
}
impl HasOpCode for Collection {
fn op_code(&self) -> OpCode {
match self {
Collection::BoolConstants(_) => OpCode::COLL_OF_BOOL_CONST,
Collection::Exprs { .. } => OpCode::COLL,
}
}
}
pub(crate) fn coll_sigma_serialize<W: SigmaByteWrite>(
coll: &Collection,
w: &mut W,
) -> SigmaSerializeResult {
match coll {
Collection::BoolConstants(bools) => {
w.put_u16(bools.len() as u16)?;
w.put_bits(bools.as_slice())?;
Ok(())
}
Collection::Exprs { elem_tpe, items } => {
w.put_u16(items.len() as u16)?;
elem_tpe.sigma_serialize(w)?;
items.iter().try_for_each(|i| i.sigma_serialize(w))
}
}
}
pub(crate) fn coll_sigma_parse<R: SigmaByteRead>(
r: &mut R,
) -> Result<Collection, SigmaParsingError> {
let items_count = r.get_u16()?;
let elem_tpe = SType::sigma_parse(r)?;
let mut items = Vec::with_capacity(items_count as usize);
for _ in 0..items_count {
items.push(Expr::sigma_parse(r)?);
}
Ok(Collection::Exprs { elem_tpe, items })
}
pub(crate) fn bool_const_coll_sigma_parse<R: SigmaByteRead>(
r: &mut R,
) -> Result<Collection, SigmaParsingError> {
let items_count = r.get_u16()?;
let bools = r.get_bits(items_count as usize)?;
Ok(Collection::BoolConstants(bools))
}
#[cfg(feature = "arbitrary")]
#[allow(clippy::unwrap_used)]
mod arbitrary {
use crate::mir::constant::arbitrary::ArbConstantParams;
use crate::mir::expr::arbitrary::ArbExprParams;
use super::*;
use proptest::collection::*;
use proptest::prelude::*;
impl Arbitrary for Collection {
type Strategy = BoxedStrategy<Self>;
type Parameters = ArbExprParams;
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
prop_oneof![
vec(any_with::<Expr>(args.clone()), 0..19),
vec(
any_with::<Constant>(ArbConstantParams::Exact(args.tpe.clone()))
.prop_map_into(),
0..19
)
]
.prop_map(move |items| Self::new(args.clone().tpe, items).unwrap())
.boxed()
}
}
}
#[cfg(test)]
#[cfg(feature = "arbitrary")]
#[allow(clippy::panic)]
mod tests {
use super::*;
use crate::mir::expr::arbitrary::ArbExprParams;
use crate::serialization::sigma_serialize_roundtrip;
use proptest::prelude::*;
proptest! {
#[test]
fn ser_roundtrip(v in any::<Collection>()) {
let expr: Expr = v.into();
prop_assert_eq![sigma_serialize_roundtrip(&expr), expr];
}
#[test]
fn ser_roundtrip_bool_const(v in any_with::<Collection>(ArbExprParams{tpe: SType::SBoolean, depth: 0})) {
let expr: Expr = v.into();
prop_assert_eq![sigma_serialize_roundtrip(&expr), expr];
}
}
}