ergotree_ir/mir/
coll_map.rs1use crate::serialization::op_code::OpCode;
2use crate::serialization::sigma_byte_reader::SigmaByteRead;
3use crate::serialization::sigma_byte_writer::SigmaByteWrite;
4use crate::serialization::SigmaParsingError;
5use crate::serialization::SigmaSerializable;
6use crate::serialization::SigmaSerializeResult;
7use crate::types::sfunc::SFunc;
8use crate::types::stype::SType;
9
10use super::expr::Expr;
11use super::expr::InvalidArgumentError;
12use crate::has_opcode::HasStaticOpCode;
13
14#[derive(PartialEq, Eq, Debug, Clone)]
16pub struct Map {
17 pub input: Box<Expr>,
19 pub mapper: Box<Expr>,
21 pub mapper_sfunc: SFunc,
23}
24
25impl Map {
26 pub fn new(input: Expr, mapper: Expr) -> Result<Self, InvalidArgumentError> {
28 let input_elem_type = match input.post_eval_tpe() {
29 SType::SColl(elem_type) => Ok(elem_type),
30 _ => Err(InvalidArgumentError(format!(
31 "Expected Map input to be SColl, got {0:?}",
32 input.tpe()
33 ))),
34 }?;
35 match mapper.tpe() {
36 SType::SFunc(sfunc) if sfunc.t_dom.len() == 1 && sfunc.t_dom[0] == *input_elem_type => {
37 Ok(Map {
38 input: input.into(),
39 mapper: mapper.into(),
40 mapper_sfunc: sfunc,
41 })
42 }
43 _ => Err(InvalidArgumentError(format!(
44 "Invalid mapper tpe: {0:?}",
45 mapper.tpe()
46 ))),
47 }
48 }
49
50 pub fn tpe(&self) -> SType {
52 SType::SColl(self.mapper_sfunc.t_range.clone().into())
53 }
54
55 pub fn out_elem_tpe(&self) -> SType {
57 *self.mapper_sfunc.t_range.clone()
58 }
59}
60
61impl HasStaticOpCode for Map {
62 const OP_CODE: OpCode = OpCode::MAP;
63}
64
65impl SigmaSerializable for Map {
66 fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> SigmaSerializeResult {
67 self.input.sigma_serialize(w)?;
68 self.mapper.sigma_serialize(w)
69 }
70
71 fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SigmaParsingError> {
72 let input = Expr::sigma_parse(r)?;
73 let mapper = Expr::sigma_parse(r)?;
74 Ok(Map::new(input, mapper)?)
75 }
76}
77
78#[cfg(feature = "arbitrary")]
79#[allow(clippy::unwrap_used)]
81mod arbitrary {
82 use super::*;
83 use crate::mir::expr::arbitrary::ArbExprParams;
84 use proptest::prelude::*;
85
86 impl Arbitrary for Map {
87 type Strategy = BoxedStrategy<Self>;
88 type Parameters = ();
89
90 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
91 (
92 any_with::<Expr>(ArbExprParams {
93 tpe: SType::SColl(SType::SBoolean.into()),
94 depth: 1,
95 }),
96 any_with::<Expr>(ArbExprParams {
97 tpe: SType::SFunc(SFunc {
98 t_dom: vec![SType::SBoolean],
99 t_range: SType::SBoolean.into(),
100 tpe_params: vec![],
101 }),
102 depth: 0,
103 }),
104 )
105 .prop_map(|(input, mapper)| Map::new(input, mapper).unwrap())
106 .boxed()
107 }
108 }
109}
110
111#[cfg(test)]
112#[cfg(feature = "arbitrary")]
113#[allow(clippy::panic)]
114mod tests {
115 use super::*;
116 use crate::mir::expr::Expr;
117 use crate::serialization::sigma_serialize_roundtrip;
118 use proptest::prelude::*;
119
120 proptest! {
121
122 #![proptest_config(ProptestConfig::with_cases(16))]
123
124 #[test]
125 fn ser_roundtrip(v in any::<Map>()) {
126 let expr: Expr = v.into();
127 prop_assert_eq![sigma_serialize_roundtrip(&expr), expr];
128 }
129 }
130}