ergotree_ir/mir/
coll_map.rs

1use 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/// Builds a new collection by applying a function to all elements of this collection.
15#[derive(PartialEq, Eq, Debug, Clone)]
16pub struct Map {
17    /// Collection
18    pub input: Box<Expr>,
19    /// Function (lambda) to apply to each element
20    pub mapper: Box<Expr>,
21    /// Type signature of the function(mapper)
22    pub mapper_sfunc: SFunc,
23}
24
25impl Map {
26    /// Create new object, returns an error if any of the requirements failed
27    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    /// Type
51    pub fn tpe(&self) -> SType {
52        SType::SColl(self.mapper_sfunc.t_range.clone().into())
53    }
54
55    /// Type of the element in the resulted collection
56    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/// Arbitrary impl
80#[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}