Skip to main content

tx3_tir/reduce/
mod.rs

1use std::collections::{BTreeMap, HashMap};
2
3use crate::model::assets::CanonicalAssets;
4use crate::model::core::*;
5use crate::model::v1beta0::*;
6
7#[derive(Debug, thiserror::Error)]
8pub enum Error {
9    #[error("invalid built-in operation {0:?}")]
10    InvalidBuiltInOp(Box<BuiltInOp>),
11
12    #[error("invalid argument {0:?} for {1}")]
13    InvalidArgument(ArgValue, String),
14
15    #[error("property {0} not found in {1}")]
16    PropertyNotFound(String, String),
17
18    #[error("property index {0} not found in {1}")]
19    PropertyIndexNotFound(usize, String),
20
21    #[error("invalid {0} operation over {1:?} and {2:?}")]
22    InvalidBinaryOp(String, String, String),
23
24    #[error("invalid {0} operation over {1:?}")]
25    InvalidUnaryOp(String, String),
26
27    #[error("cannot coerce {0:?} into assets")]
28    CannotCoerceIntoAssets(Expression),
29
30    #[error("cannot coerce {0:?} into datum")]
31    CannotCoerceIntoDatum(Expression),
32
33    #[error("compiler op failed: {0}")]
34    CompilerOpFailed(Box<crate::compile::Error>),
35}
36
37impl From<crate::compile::Error> for Error {
38    fn from(error: crate::compile::Error) -> Self {
39        Error::CompilerOpFailed(Box::new(error))
40    }
41}
42
43pub trait Indexable: std::fmt::Debug {
44    fn index(&self, index: Expression) -> Option<Expression>;
45
46    fn index_or_err(&self, index: Expression) -> Result<Expression, Error> {
47        let index_value = index.as_number().unwrap_or(0) as usize;
48        self.index(index).ok_or(Error::PropertyIndexNotFound(
49            index_value,
50            format!("{self:?}"),
51        ))
52    }
53}
54
55impl Indexable for StructExpr {
56    fn index(&self, index: Expression) -> Option<Expression> {
57        // numeric indices represent the index of the field of the struct
58        match index {
59            Expression::Number(n) => self.fields.get(n as usize).cloned(),
60            _ => return None,
61        }
62    }
63}
64
65impl Indexable for Expression {
66    fn index(&self, index: Expression) -> Option<Expression> {
67        match self {
68            Expression::None => None,
69            Expression::Map(x) => x
70                .iter()
71                .find(|(k, _)| *k == index)
72                .map(|(k, v)| Expression::Tuple(Box::new((k.clone(), v.clone())))),
73            Expression::List(x) => x.get(index.as_number()? as usize).cloned(),
74            Expression::Tuple(x) => match index.as_number()? {
75                0 => Some(x.0.clone()),
76                1 => Some(x.1.clone()),
77                _ => None,
78            },
79            Expression::Struct(x) => x.index(index.clone()),
80            _ => None,
81        }
82    }
83}
84
85pub trait Concatenable {
86    fn concat(self, other: Expression) -> Result<Expression, Error>;
87}
88
89pub trait Arithmetic {
90    fn add(self, other: Expression) -> Result<Expression, Error>;
91    fn sub(self, other: Expression) -> Result<Expression, Error>;
92    fn neg(self) -> Result<Expression, Error>;
93}
94
95impl<T> Arithmetic for T
96where
97    T: Into<CanonicalAssets> + std::fmt::Debug,
98{
99    fn add(self, other: Expression) -> Result<Expression, Error> {
100        let y = match other {
101            Expression::Assets(x) => CanonicalAssets::from(x),
102            Expression::None => CanonicalAssets::empty(),
103            other => {
104                return Err(Error::InvalidBinaryOp(
105                    "add".to_string(),
106                    format!("{self:?}"),
107                    format!("{other:?}"),
108                ))
109            }
110        };
111
112        let x = self.into();
113        let total = x + y;
114        Ok(Expression::Assets(total.into()))
115    }
116
117    fn sub(self, other: Expression) -> Result<Expression, Error> {
118        let other_neg = other.neg()?;
119        self.add(other_neg)
120    }
121
122    fn neg(self) -> Result<Expression, Error> {
123        let negated = std::ops::Neg::neg(self.into());
124        Ok(Expression::Assets(negated.into()))
125    }
126}
127
128impl Arithmetic for i128 {
129    fn add(self, other: Expression) -> Result<Expression, Error> {
130        match other {
131            Expression::Number(y) => Ok(Expression::Number(self + y)),
132            Expression::None => Ok(Expression::Number(self)),
133            _ => Err(Error::InvalidBinaryOp(
134                "add".to_string(),
135                format!("{self:?}"),
136                format!("{other:?}"),
137            )),
138        }
139    }
140
141    fn sub(self, other: Expression) -> Result<Expression, Error> {
142        let other_neg = other.neg()?;
143        self.add(other_neg)
144    }
145
146    fn neg(self) -> Result<Expression, Error> {
147        Ok(Expression::Number(-self))
148    }
149}
150
151impl Arithmetic for Expression {
152    fn add(self, other: Expression) -> Result<Expression, Error> {
153        match self {
154            Expression::None => Ok(other),
155            Expression::Number(x) => Arithmetic::add(x, other),
156            Expression::Assets(x) => Arithmetic::add(x, other),
157            x => Err(Error::InvalidBinaryOp(
158                "add".to_string(),
159                format!("{x:?}"),
160                format!("{other:?}"),
161            )),
162        }
163    }
164
165    fn sub(self, other: Expression) -> Result<Expression, Error> {
166        match self {
167            Expression::None => Ok(other),
168            Expression::Number(x) => Arithmetic::sub(x, other),
169            Expression::Assets(x) => Arithmetic::sub(x, other),
170            x => Err(Error::InvalidBinaryOp(
171                "sub".to_string(),
172                format!("{x:?}"),
173                format!("{other:?}"),
174            )),
175        }
176    }
177
178    fn neg(self) -> Result<Expression, Error> {
179        match self {
180            Expression::None => Ok(Expression::None),
181            Expression::Number(x) => Arithmetic::neg(x),
182            Expression::Assets(x) => Arithmetic::neg(x),
183            x => Err(Error::InvalidUnaryOp("neg".to_string(), format!("{x:?}"))),
184        }
185    }
186}
187
188impl Concatenable for String {
189    fn concat(self, other: Expression) -> Result<Expression, Error> {
190        match other {
191            Expression::String(y) => Ok(Expression::String(self + &y)),
192            Expression::Number(y) => Ok(Expression::String(self + &y.to_string())),
193            Expression::None => Ok(Expression::String(self)),
194            _ => Err(Error::InvalidBinaryOp(
195                "concat".to_string(),
196                format!("String({self:?})"),
197                format!("{other:?}"),
198            )),
199        }
200    }
201}
202
203impl Concatenable for Vec<Expression> {
204    fn concat(self, other: Expression) -> Result<Expression, Error> {
205        match other {
206            Expression::List(expressions) => {
207                Ok(Expression::List([&self[..], &expressions[..]].concat()))
208            }
209            _ => Err(Error::InvalidBinaryOp(
210                "concat".to_string(),
211                format!("List({:?})", self),
212                format!("{:?}", other),
213            )),
214        }
215    }
216}
217
218impl Concatenable for Vec<u8> {
219    fn concat(self, other: Expression) -> Result<Expression, Error> {
220        match other {
221            Expression::Bytes(y) => {
222                let mut result = self;
223                result.extend(y);
224                Ok(Expression::Bytes(result))
225            }
226            Expression::None => Ok(Expression::Bytes(self)),
227            _ => Err(Error::InvalidBinaryOp(
228                "concat".to_string(),
229                format!("Bytes({self:?})"),
230                format!("{other:?}"),
231            )),
232        }
233    }
234}
235
236impl Concatenable for Expression {
237    fn concat(self, other: Expression) -> Result<Expression, Error> {
238        match self {
239            Expression::None => Ok(other),
240            Expression::String(x) => Concatenable::concat(x, other),
241            Expression::Bytes(x) => Concatenable::concat(x, other),
242            Expression::List(x) => Concatenable::concat(x, other),
243            x => Err(Error::InvalidBinaryOp(
244                "concat".to_string(),
245                format!("{x:?}"),
246                format!("{other:?}"),
247            )),
248        }
249    }
250}
251
252pub trait Coerceable {
253    fn into_assets(self) -> Result<Expression, Error>;
254    fn into_datum(self) -> Result<Expression, Error>;
255}
256
257impl Coerceable for Expression {
258    fn into_assets(self) -> Result<Expression, Error> {
259        match self {
260            Expression::None => Ok(Expression::None),
261            Expression::Assets(x) => Ok(Expression::Assets(x)),
262            Expression::UtxoSet(x) => Ok(Expression::Assets(x.total_assets().into())),
263            _ => Err(Error::CannotCoerceIntoAssets(self)),
264        }
265    }
266
267    fn into_datum(self) -> Result<Expression, Error> {
268        match self {
269            Expression::None => Ok(Expression::None),
270            Expression::UtxoSet(x) => Ok(x
271                .first_by_ref()
272                .and_then(|utxo| utxo.datum.clone())
273                .unwrap_or(Expression::None)),
274            Expression::List(x) => Ok(Expression::List(x)),
275            Expression::Map(x) => Ok(Expression::Map(x)),
276            Expression::Tuple(x) => Ok(Expression::Tuple(x)),
277            Expression::Struct(x) => Ok(Expression::Struct(x)),
278            Expression::Bytes(x) => Ok(Expression::Bytes(x)),
279            Expression::Number(x) => Ok(Expression::Number(x)),
280            Expression::String(x) => Ok(Expression::String(x)),
281            Expression::Address(x) => Ok(Expression::Bytes(x)),
282            Expression::Hash(x) => Ok(Expression::Bytes(x)),
283            _ => Err(Error::CannotCoerceIntoDatum(self)),
284        }
285    }
286}
287
288fn arg_value_into_expr(arg: ArgValue) -> Expression {
289    match arg {
290        ArgValue::Address(x) => Expression::Address(x),
291        ArgValue::Int(x) => Expression::Number(x),
292        ArgValue::Bool(x) => Expression::Bool(x),
293        ArgValue::String(x) => Expression::String(x),
294        ArgValue::Bytes(x) => Expression::Bytes(x),
295        ArgValue::UtxoSet(x) => Expression::UtxoSet(x),
296        ArgValue::UtxoRef(x) => Expression::UtxoRefs(vec![x]),
297    }
298}
299
300pub trait Apply: Sized + std::fmt::Debug {
301    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error>;
302    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error>;
303    fn apply_fees(self, fees: u64) -> Result<Self, Error>;
304
305    fn is_constant(&self) -> bool;
306
307    fn params(&self) -> BTreeMap<String, Type>;
308    fn queries(&self) -> BTreeMap<String, InputQuery>;
309
310    fn reduce(self) -> Result<Self, Error>;
311}
312
313pub trait Composite: Sized {
314    fn reduce_self(self) -> Result<Self, Error> {
315        Ok(self)
316    }
317
318    fn components(&self) -> Vec<&Expression>;
319
320    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
321    where
322        F: Fn(Expression) -> Result<Expression, Error> + Clone;
323
324    fn reduce_nested(self) -> Result<Self, Error> {
325        self.try_map_components(|x| x.reduce())
326    }
327}
328
329impl<T> Apply for T
330where
331    T: Composite + std::fmt::Debug,
332{
333    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
334        self.try_map_components(|x| x.apply_args(args))
335    }
336
337    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
338        self.try_map_components(|x| x.apply_inputs(args))
339    }
340
341    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
342        self.try_map_components(|x| x.apply_fees(fees))
343    }
344
345    fn is_constant(&self) -> bool {
346        self.components().iter().all(|x| x.is_constant())
347    }
348
349    fn params(&self) -> BTreeMap<String, Type> {
350        self.components().iter().flat_map(|x| x.params()).collect()
351    }
352
353    fn queries(&self) -> BTreeMap<String, InputQuery> {
354        self.components().iter().flat_map(|x| x.queries()).collect()
355    }
356
357    fn reduce(self) -> Result<Self, Error> {
358        let x = self.reduce_nested()?;
359
360        if x.is_constant() {
361            x.reduce_self()
362        } else {
363            Ok(x)
364        }
365    }
366}
367
368impl<T> Apply for Option<T>
369where
370    T: Apply,
371{
372    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
373        self.map(|x| x.apply_args(args)).transpose()
374    }
375
376    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
377        self.map(|x| x.apply_inputs(args)).transpose()
378    }
379
380    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
381        self.map(|x| x.apply_fees(fees)).transpose()
382    }
383
384    fn is_constant(&self) -> bool {
385        match self {
386            Some(x) => x.is_constant(),
387            None => true,
388        }
389    }
390
391    fn params(&self) -> BTreeMap<String, Type> {
392        match self {
393            Some(x) => x.params(),
394            None => BTreeMap::new(),
395        }
396    }
397
398    fn queries(&self) -> BTreeMap<String, InputQuery> {
399        match self {
400            Some(x) => x.queries(),
401            None => BTreeMap::new(),
402        }
403    }
404
405    fn reduce(self) -> Result<Self, Error> {
406        self.map(|x| x.reduce()).transpose()
407    }
408}
409
410impl<T> Apply for Vec<T>
411where
412    T: Apply,
413{
414    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
415        self.into_iter().map(|x| x.apply_args(args)).collect()
416    }
417
418    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
419        self.into_iter().map(|x| x.apply_inputs(args)).collect()
420    }
421
422    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
423        self.into_iter().map(|x| x.apply_fees(fees)).collect()
424    }
425
426    fn is_constant(&self) -> bool {
427        self.iter().all(|x| x.is_constant())
428    }
429
430    fn params(&self) -> BTreeMap<String, Type> {
431        self.iter().flat_map(|x| x.params()).collect()
432    }
433
434    fn queries(&self) -> BTreeMap<String, InputQuery> {
435        self.iter().flat_map(|x| x.queries()).collect()
436    }
437
438    fn reduce(self) -> Result<Self, Error> {
439        self.into_iter().map(|x| x.reduce()).collect()
440    }
441}
442
443impl<T> Apply for HashMap<String, T>
444where
445    T: Apply,
446{
447    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
448        self.into_iter()
449            .map(|(k, v)| v.apply_args(args).map(|v| (k, v)))
450            .collect()
451    }
452
453    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
454        self.into_iter()
455            .map(|(k, v)| v.apply_inputs(args).map(|v| (k, v)))
456            .collect()
457    }
458
459    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
460        self.into_iter()
461            .map(|(k, v)| v.apply_fees(fees).map(|v| (k, v)))
462            .collect()
463    }
464
465    fn is_constant(&self) -> bool {
466        self.values().all(|x| x.is_constant())
467    }
468
469    fn params(&self) -> BTreeMap<String, Type> {
470        self.values().flat_map(|x| x.params()).collect()
471    }
472
473    fn queries(&self) -> BTreeMap<String, InputQuery> {
474        self.values().flat_map(|x| x.queries()).collect()
475    }
476
477    fn reduce(self) -> Result<Self, Error> {
478        self.into_iter()
479            .map(|(k, v)| v.reduce().map(|v| (k, v)))
480            .collect()
481    }
482}
483
484impl Composite for ScriptSource {
485    fn reduce_self(self) -> Result<Self, Error> {
486        Ok(self)
487    }
488
489    fn components(&self) -> Vec<&Expression> {
490        match self {
491            ScriptSource::Embedded(x) => vec![x],
492            ScriptSource::UtxoRef { r#ref, source } => {
493                std::iter::once(r#ref).chain(source.as_ref()).collect()
494            }
495        }
496    }
497
498    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
499    where
500        F: Fn(Expression) -> Result<Expression, Error> + Clone,
501    {
502        match self {
503            ScriptSource::Embedded(x) => Ok(ScriptSource::Embedded(f(x)?)),
504            ScriptSource::UtxoRef { r#ref, source } => Ok(ScriptSource::UtxoRef {
505                r#ref: f(r#ref)?,
506                source: source.map(&f).transpose()?,
507            }),
508        }
509    }
510}
511
512impl TryFrom<&ArgValue> for ScriptSource {
513    type Error = Error;
514
515    fn try_from(value: &ArgValue) -> Result<Self, Self::Error> {
516        match value {
517            ArgValue::Bytes(x) => Ok(ScriptSource::Embedded(Expression::Bytes(x.clone()))),
518            ArgValue::UtxoRef(x) => Ok(ScriptSource::UtxoRef {
519                r#ref: Expression::UtxoRefs(vec![x.clone()]),
520                source: None,
521            }),
522            _ => Err(Error::InvalidArgument(value.clone(), "script".to_string())),
523        }
524    }
525}
526
527impl Composite for PolicyExpr {
528    fn components(&self) -> Vec<&Expression> {
529        let script = self.script.components();
530        std::iter::once(&self.hash).chain(script).collect()
531    }
532
533    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
534    where
535        F: Fn(Expression) -> Result<Expression, Error> + Clone,
536    {
537        Ok(Self {
538            name: self.name,
539            hash: f(self.hash)?,
540            script: self.script.try_map_components(f)?,
541        })
542    }
543}
544
545impl Composite for StructExpr {
546    fn components(&self) -> Vec<&Expression> {
547        self.fields.iter().collect()
548    }
549
550    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
551    where
552        F: Fn(Expression) -> Result<Expression, Error> + Clone,
553    {
554        Ok(Self {
555            constructor: self.constructor,
556            fields: self
557                .fields
558                .into_iter()
559                .map(&f)
560                .collect::<Result<Vec<_>, _>>()?,
561        })
562    }
563}
564
565impl Composite for AssetExpr {
566    fn components(&self) -> Vec<&Expression> {
567        vec![&self.policy, &self.asset_name, &self.amount]
568    }
569
570    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
571    where
572        F: Fn(Expression) -> Result<Expression, Error> + Clone,
573    {
574        Ok(Self {
575            policy: f(self.policy)?,
576            asset_name: f(self.asset_name)?,
577            amount: f(self.amount)?,
578        })
579    }
580}
581
582impl Composite for Coerce {
583    fn components(&self) -> Vec<&Expression> {
584        match self {
585            Self::IntoAssets(x) => vec![x],
586            Self::IntoDatum(x) => vec![x],
587            Self::IntoScript(x) => vec![x],
588            Self::NoOp(x) => vec![x],
589        }
590    }
591
592    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
593    where
594        F: Fn(Expression) -> Result<Expression, Error> + Clone,
595    {
596        match self {
597            Self::IntoAssets(x) => Ok(Self::IntoAssets(f(x)?)),
598            Self::IntoDatum(x) => Ok(Self::IntoDatum(f(x)?)),
599            Self::IntoScript(x) => Ok(Self::IntoScript(f(x)?)),
600            Self::NoOp(x) => Ok(Self::NoOp(f(x)?)),
601        }
602    }
603
604    fn reduce_self(self) -> Result<Self, Error> {
605        match self {
606            Self::NoOp(x) => Ok(Self::NoOp(x)),
607            Self::IntoAssets(x) => Ok(Self::NoOp(x.into_assets()?)),
608            Self::IntoDatum(x) => Ok(Self::NoOp(x.into_datum()?)),
609            Self::IntoScript(x) => todo!(),
610        }
611    }
612}
613
614impl Composite for BuiltInOp {
615    fn components(&self) -> Vec<&Expression> {
616        match self {
617            Self::NoOp(x) => vec![x],
618            Self::Add(x, y) => vec![x, y],
619            Self::Sub(x, y) => vec![x, y],
620            Self::Concat(x, y) => vec![x, y],
621            Self::Negate(x) => vec![x],
622            Self::Property(x, _) => vec![x],
623        }
624    }
625
626    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
627    where
628        F: Fn(Expression) -> Result<Expression, Error> + Clone,
629    {
630        match self {
631            Self::NoOp(x) => Ok(Self::NoOp(f(x)?)),
632            Self::Add(x, y) => Ok(Self::Add(f(x)?, f(y)?)),
633            Self::Sub(x, y) => Ok(Self::Sub(f(x)?, f(y)?)),
634            Self::Concat(x, y) => Ok(Self::Concat(f(x)?, f(y)?)),
635            Self::Negate(x) => Ok(Self::Negate(f(x)?)),
636            Self::Property(x, prop) => Ok(Self::Property(f(x)?, prop)),
637        }
638    }
639
640    fn reduce_self(self) -> Result<Self, Error> {
641        match self {
642            Self::Add(x, y) => Ok(Self::NoOp(x.add(y)?)),
643            Self::Sub(x, y) => Ok(Self::NoOp(x.sub(y)?)),
644            Self::Concat(x, y) => Ok(Self::NoOp(x.concat(y)?)),
645            Self::Negate(x) => Ok(Self::NoOp(x.neg()?)),
646            Self::Property(x, prop) => Ok(Self::NoOp(x.index_or_err(prop)?)),
647            Self::NoOp(x) => Ok(Self::NoOp(x)),
648        }
649    }
650
651    fn reduce_nested(self) -> Result<Self, Error> {
652        match self {
653            Self::Add(x, y) => Ok(Self::Add(x.reduce()?, y.reduce()?)),
654            Self::Sub(x, y) => Ok(Self::Sub(x.reduce()?, y.reduce()?)),
655            Self::Concat(x, y) => Ok(Self::Concat(x.reduce()?, y.reduce()?)),
656            Self::Negate(x) => Ok(Self::Negate(x.reduce()?)),
657            Self::Property(x, y) => Ok(Self::Property(x.reduce()?, y.reduce()?)),
658            Self::NoOp(x) => Ok(Self::NoOp(x.reduce()?)),
659        }
660    }
661}
662
663impl From<AssetExpr> for CanonicalAssets {
664    fn from(asset: AssetExpr) -> Self {
665        let policy = asset.expect_constant_policy();
666        let name = asset.expect_constant_name();
667        let amount = asset.expect_constant_amount();
668
669        Self::from_asset(policy, name, amount)
670    }
671}
672
673impl From<Vec<AssetExpr>> for CanonicalAssets {
674    fn from(assets: Vec<AssetExpr>) -> Self {
675        let mut result = CanonicalAssets::empty();
676
677        for asset in assets {
678            let asset = asset.into();
679            result = result + asset;
680        }
681
682        result
683    }
684}
685
686impl From<CanonicalAssets> for Vec<AssetExpr> {
687    fn from(assets: CanonicalAssets) -> Self {
688        let mut result = Vec::new();
689
690        for (class, amount) in assets.into_iter() {
691            result.push(AssetExpr {
692                policy: class
693                    .policy()
694                    .map(|x| Expression::Bytes(x.to_vec()))
695                    .unwrap_or(Expression::None),
696                asset_name: class
697                    .name()
698                    .map(|x| Expression::Bytes(x.to_vec()))
699                    .unwrap_or(Expression::None),
700                amount: Expression::Number(amount),
701            });
702        }
703
704        result
705    }
706}
707
708impl AssetExpr {
709    fn expect_constant_policy(&self) -> Option<&[u8]> {
710        match &self.policy {
711            Expression::None => None,
712            Expression::Bytes(x) => Some(x.as_slice()),
713            _ => None,
714        }
715    }
716
717    fn expect_constant_name(&self) -> Option<&[u8]> {
718        match &self.asset_name {
719            Expression::None => None,
720            Expression::Bytes(x) => Some(x.as_slice()),
721            Expression::String(x) => Some(x.as_bytes()),
722            _ => None,
723        }
724    }
725
726    fn expect_constant_amount(&self) -> i128 {
727        match &self.amount {
728            Expression::Number(x) => *x,
729            _ => unreachable!("amount expected to be Number"),
730        }
731    }
732}
733
734impl Composite for Input {
735    fn components(&self) -> Vec<&Expression> {
736        vec![&self.utxos, &self.redeemer]
737    }
738
739    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
740    where
741        F: Fn(Expression) -> Result<Expression, Error> + Clone,
742    {
743        Ok(Self {
744            name: self.name,
745            utxos: f(self.utxos)?,
746            redeemer: f(self.redeemer)?,
747        })
748    }
749}
750
751impl Composite for InputQuery {
752    fn components(&self) -> Vec<&Expression> {
753        vec![&self.address, &self.min_amount, &self.r#ref]
754    }
755
756    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
757    where
758        F: Fn(Expression) -> Result<Expression, Error> + Clone,
759    {
760        Ok(Self {
761            address: f(self.address)?,
762            min_amount: f(self.min_amount)?,
763            r#ref: f(self.r#ref)?,
764            ..self
765        })
766    }
767}
768
769impl Apply for Param {
770    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
771        match self {
772            Param::ExpectValue(name, ty) => {
773                let defined = args.get(&name).cloned();
774
775                match defined {
776                    Some(x) => Ok(Param::Set(arg_value_into_expr(x))),
777                    None => Ok(Self::ExpectValue(name, ty)),
778                }
779            }
780            // queries can have nested params
781            Param::ExpectInput(name, query) => {
782                Ok(Param::ExpectInput(name, query.apply_args(args)?))
783            }
784            x => Ok(x),
785        }
786    }
787
788    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
789        match self {
790            Param::ExpectInput(name, query) => {
791                let defined = args.get(&name).cloned();
792
793                match defined {
794                    Some(x) => Ok(Param::Set(Expression::UtxoSet(x))),
795                    None => Ok(Self::ExpectInput(name, query)),
796                }
797            }
798            x => Ok(x),
799        }
800    }
801
802    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
803        match self {
804            Param::ExpectFees => Ok(Param::Set(Expression::Assets(vec![AssetExpr {
805                policy: Expression::None,
806                asset_name: Expression::None,
807                amount: Expression::Number(fees as i128),
808            }]))),
809            // queries can have nested params
810            Param::ExpectInput(name, query) => {
811                Ok(Param::ExpectInput(name, query.apply_fees(fees)?))
812            }
813            x => Ok(x),
814        }
815    }
816
817    fn is_constant(&self) -> bool {
818        match self {
819            Param::Set(x) => x.is_constant(),
820            _ => false,
821        }
822    }
823
824    fn params(&self) -> BTreeMap<String, Type> {
825        match self {
826            Param::ExpectValue(name, ty) => BTreeMap::from([(name.clone(), ty.clone())]),
827            // queries can have nested params
828            Param::ExpectInput(_, x) => x.params(),
829            _ => BTreeMap::new(),
830        }
831    }
832
833    fn queries(&self) -> BTreeMap<String, InputQuery> {
834        match self {
835            Param::ExpectInput(name, query) => BTreeMap::from([(name.clone(), query.clone())]),
836            _ => BTreeMap::new(),
837        }
838    }
839
840    fn reduce(self) -> Result<Self, Error> {
841        match self {
842            // queries can have nested expressions that need to be reduced
843            Param::ExpectInput(name, query) => Ok(Param::ExpectInput(name, query.reduce()?)),
844            x => Ok(x),
845        }
846    }
847}
848
849impl Apply for Expression {
850    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
851        match self {
852            Self::List(x) => Ok(Self::List(
853                x.into_iter()
854                    .map(|x| x.apply_args(args))
855                    .collect::<Result<_, _>>()?,
856            )),
857            Self::Tuple(x) => Ok(Self::Tuple(Box::new((
858                x.0.apply_args(args)?,
859                x.1.apply_args(args)?,
860            )))),
861            Self::Map(x) => Ok(Self::Map(
862                x.into_iter()
863                    .map(|(k, v)| {
864                        Ok::<(Expression, Expression), Error>((
865                            k.apply_args(args)?,
866                            v.apply_args(args)?,
867                        ))
868                    })
869                    .collect::<Result<_, _>>()?,
870            )),
871            Self::Struct(x) => Ok(Self::Struct(x.apply_args(args)?)),
872            Self::Assets(x) => Ok(Self::Assets(
873                x.into_iter()
874                    .map(|x| x.apply_args(args))
875                    .collect::<Result<_, _>>()?,
876            )),
877            Self::EvalParam(x) => Ok(Self::EvalParam(Box::new(x.apply_args(args)?))),
878            Self::EvalBuiltIn(x) => Ok(Self::EvalBuiltIn(Box::new(x.apply_args(args)?))),
879            Self::EvalCoerce(x) => Ok(Self::EvalCoerce(Box::new(x.apply_args(args)?))),
880            Self::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.apply_args(args)?))),
881            Self::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.apply_args(args)?))),
882
883            // Don't fall into the temptation of simplifying the following cases under a single
884            // wildcard with a default implementation, it makes it really hard to detect missing
885            // implementation when adding new `Expression` variants.
886            Self::None => Ok(self),
887            Self::Bytes(_) => Ok(self),
888            Self::Number(_) => Ok(self),
889            Self::Bool(_) => Ok(self),
890            Self::String(_) => Ok(self),
891            Self::Address(_) => Ok(self),
892            Self::Hash(_) => Ok(self),
893            Self::UtxoRefs(_) => Ok(self),
894            Self::UtxoSet(_) => Ok(self),
895        }
896    }
897
898    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
899        match self {
900            Self::List(x) => Ok(Self::List(
901                x.into_iter()
902                    .map(|x| x.apply_inputs(args))
903                    .collect::<Result<_, _>>()?,
904            )),
905            Self::Map(x) => Ok(Self::Map(
906                x.into_iter()
907                    .map(|(k, v)| {
908                        Ok::<(Expression, Expression), Error>((
909                            k.apply_inputs(args)?,
910                            v.apply_inputs(args)?,
911                        ))
912                    })
913                    .collect::<Result<_, _>>()?,
914            )),
915            Self::Tuple(x) => Ok(Self::Tuple(Box::new((
916                x.0.apply_inputs(args)?,
917                x.1.apply_inputs(args)?,
918            )))),
919            Self::Struct(x) => Ok(Self::Struct(x.apply_inputs(args)?)),
920            Self::Assets(x) => Ok(Self::Assets(
921                x.into_iter()
922                    .map(|x| x.apply_inputs(args))
923                    .collect::<Result<_, _>>()?,
924            )),
925            Self::EvalParam(x) => Ok(Self::EvalParam(Box::new(x.apply_inputs(args)?))),
926            Self::EvalBuiltIn(x) => Ok(Self::EvalBuiltIn(Box::new(x.apply_inputs(args)?))),
927            Self::EvalCoerce(x) => Ok(Self::EvalCoerce(Box::new(x.apply_inputs(args)?))),
928            Self::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.apply_inputs(args)?))),
929            Self::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.apply_inputs(args)?))),
930
931            // Don't fall into the temptation of simplifying the following cases under a single
932            // wildcard with a default implementation, it makes it really hard to detect missing
933            // implementation when adding new `Expression` variants.
934            Self::None => Ok(self),
935            Self::Bytes(_) => Ok(self),
936            Self::Number(_) => Ok(self),
937            Self::Bool(_) => Ok(self),
938            Self::String(_) => Ok(self),
939            Self::Address(_) => Ok(self),
940            Self::Hash(_) => Ok(self),
941            Self::UtxoRefs(_) => Ok(self),
942            Self::UtxoSet(_) => Ok(self),
943        }
944    }
945
946    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
947        match self {
948            Self::List(x) => Ok(Self::List(
949                x.into_iter()
950                    .map(|x| x.apply_fees(fees))
951                    .collect::<Result<_, _>>()?,
952            )),
953            Self::Map(x) => Ok(Self::Map(
954                x.into_iter()
955                    .map(|(k, v)| {
956                        Ok::<(Expression, Expression), Error>((
957                            k.apply_fees(fees)?,
958                            v.apply_fees(fees)?,
959                        ))
960                    })
961                    .collect::<Result<_, _>>()?,
962            )),
963            Self::Tuple(x) => Ok(Self::Tuple(Box::new((
964                x.0.apply_fees(fees)?,
965                x.1.apply_fees(fees)?,
966            )))),
967            Self::Struct(x) => Ok(Self::Struct(x.apply_fees(fees)?)),
968            Self::Assets(x) => Ok(Self::Assets(
969                x.into_iter()
970                    .map(|x| x.apply_fees(fees))
971                    .collect::<Result<_, _>>()?,
972            )),
973            Self::EvalParam(x) => Ok(Self::EvalParam(Box::new(x.apply_fees(fees)?))),
974            Self::EvalBuiltIn(x) => Ok(Self::EvalBuiltIn(Box::new(x.apply_fees(fees)?))),
975            Self::EvalCoerce(x) => Ok(Self::EvalCoerce(Box::new(x.apply_fees(fees)?))),
976            Self::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.apply_fees(fees)?))),
977            Self::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.apply_fees(fees)?))),
978
979            // Don't fall into the temptation of simplifying the following cases under a single
980            // wildcard with a default implementation, it makes it really hard to detect missing
981            // implementation when adding new `Expression` variants.
982            Self::None => Ok(self),
983            Self::Bytes(_) => Ok(self),
984            Self::Number(_) => Ok(self),
985            Self::Bool(_) => Ok(self),
986            Self::String(_) => Ok(self),
987            Self::Address(_) => Ok(self),
988            Self::Hash(_) => Ok(self),
989            Self::UtxoRefs(_) => Ok(self),
990            Self::UtxoSet(_) => Ok(self),
991        }
992    }
993
994    fn is_constant(&self) -> bool {
995        match self {
996            Self::List(x) => x.iter().all(|x| x.is_constant()),
997            Self::Map(x) => x.iter().all(|(k, v)| k.is_constant() && v.is_constant()),
998            Self::Tuple(x) => x.0.is_constant() && x.1.is_constant(),
999            Self::Struct(x) => x.is_constant(),
1000            Self::Assets(x) => x.iter().all(|x| x.is_constant()),
1001            Self::EvalParam(x) => x.is_constant(),
1002            Self::EvalBuiltIn(x) => x.is_constant(),
1003            Self::EvalCoerce(x) => x.is_constant(),
1004            Self::EvalCompiler(_) => false,
1005            Self::AdHocDirective(x) => x.is_constant(),
1006
1007            // Don't fall into the temptation of simplifying the following cases under a single
1008            // wildcard with a default implementation, it makes it really hard to detect missing
1009            // implementation when adding new `Expression` variants.
1010            Self::None => true,
1011            Self::Bytes(_) => true,
1012            Self::Number(_) => true,
1013            Self::Bool(_) => true,
1014            Self::String(_) => true,
1015            Self::Address(_) => true,
1016            Self::Hash(_) => true,
1017            Self::UtxoRefs(_) => true,
1018            Self::UtxoSet(_) => true,
1019        }
1020    }
1021
1022    fn params(&self) -> BTreeMap<String, Type> {
1023        match self {
1024            Self::List(x) => x.iter().flat_map(|x| x.params()).collect(),
1025            Self::Map(x) => x
1026                .iter()
1027                .flat_map(|(k, v)| [k.params(), v.params()].into_iter().flatten())
1028                .collect(),
1029            Self::Tuple(x) => [x.0.params(), x.1.params()].into_iter().flatten().collect(),
1030            Self::Struct(x) => x.params(),
1031            Self::Assets(x) => x.iter().flat_map(|x| x.params()).collect(),
1032            Self::EvalParam(x) => x.params(),
1033            Self::EvalBuiltIn(x) => x.params(),
1034            Self::EvalCoerce(x) => x.params(),
1035            Self::EvalCompiler(x) => x.params(),
1036            Self::AdHocDirective(x) => x.params(),
1037
1038            // Don't fall into the temptation of simplifying the following cases under a single
1039            // wildcard with a default implementation, it makes it really hard to detect missing
1040            // implementation when adding new `Expression` variants.
1041            Self::None => BTreeMap::new(),
1042            Self::Bytes(_) => BTreeMap::new(),
1043            Self::Number(_) => BTreeMap::new(),
1044            Self::Bool(_) => BTreeMap::new(),
1045            Self::String(_) => BTreeMap::new(),
1046            Self::Address(_) => BTreeMap::new(),
1047            Self::Hash(_) => BTreeMap::new(),
1048            Self::UtxoRefs(_) => BTreeMap::new(),
1049            Self::UtxoSet(_) => BTreeMap::new(),
1050        }
1051    }
1052
1053    fn queries(&self) -> BTreeMap<String, InputQuery> {
1054        match self {
1055            Self::List(x) => x.iter().flat_map(|x| x.queries()).collect(),
1056            Self::Map(x) => x
1057                .iter()
1058                .flat_map(|(k, v)| [k.queries(), v.queries()].into_iter().flatten())
1059                .collect(),
1060            Self::Tuple(x) => [x.0.queries(), x.1.queries()]
1061                .into_iter()
1062                .flatten()
1063                .collect(),
1064            Self::Struct(x) => x.queries(),
1065            Self::Assets(x) => x.iter().flat_map(|x| x.queries()).collect(),
1066            Self::EvalParam(x) => x.queries(),
1067            Self::EvalBuiltIn(x) => x.queries(),
1068            Self::EvalCoerce(x) => x.queries(),
1069            Self::EvalCompiler(x) => x.queries(),
1070            Self::AdHocDirective(x) => x.queries(),
1071
1072            // Don't fall into the temptation of simplifying the following cases under a single
1073            // wildcard with a default implementation, it makes it really hard to detect missing
1074            // implementation when adding new `Expression` variants.
1075            Self::None => BTreeMap::new(),
1076            Self::Bytes(_) => BTreeMap::new(),
1077            Self::Number(_) => BTreeMap::new(),
1078            Self::Bool(_) => BTreeMap::new(),
1079            Self::String(_) => BTreeMap::new(),
1080            Self::Address(_) => BTreeMap::new(),
1081            Self::Hash(_) => BTreeMap::new(),
1082            Self::UtxoRefs(_) => BTreeMap::new(),
1083            Self::UtxoSet(_) => BTreeMap::new(),
1084        }
1085    }
1086
1087    fn reduce(self) -> Result<Self, Error> {
1088        match self {
1089            // the following expressions can only be reduced internally
1090            Expression::List(x) => Ok(Self::List(
1091                x.into_iter()
1092                    .map(|x| x.reduce())
1093                    .collect::<Result<_, _>>()?,
1094            )),
1095            Expression::Map(x) => Ok(Self::Map(
1096                x.into_iter()
1097                    .map(|(k, v)| Ok::<(Expression, Expression), Error>((k.reduce()?, v.reduce()?)))
1098                    .collect::<Result<_, _>>()?,
1099            )),
1100            Expression::Tuple(x) => Ok(Self::Tuple(Box::new((x.0.reduce()?, x.1.reduce()?)))),
1101            Expression::Struct(x) => Ok(Self::Struct(x.reduce()?)),
1102            Expression::Assets(x) => Ok(Self::Assets(
1103                x.into_iter()
1104                    .map(|x| x.reduce())
1105                    .collect::<Result<_, _>>()?,
1106            )),
1107            Expression::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.reduce()?))),
1108            Expression::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.reduce()?))),
1109
1110            // the following ones can be turned into simpler expressions
1111            Expression::EvalBuiltIn(x) => match x.reduce()? {
1112                BuiltInOp::NoOp(x) => Ok(x),
1113                x => Ok(Expression::EvalBuiltIn(Box::new(x.reduce()?))),
1114            },
1115            Expression::EvalCoerce(x) => match x.reduce()? {
1116                Coerce::NoOp(x) => Ok(x),
1117                x => Ok(Expression::EvalCoerce(Box::new(x.reduce()?))),
1118            },
1119            Expression::EvalParam(x) => match x.reduce()? {
1120                Param::Set(x) => Ok(x),
1121                x => Ok(Expression::EvalParam(Box::new(x.reduce()?))),
1122            },
1123
1124            // Don't fall into the temptation of simplifying the following cases under a single
1125            // wildcard with a default implementation, it makes it really hard to detect missing
1126            // implementation when adding new `Expression` variants.
1127            Self::None => Ok(self),
1128            Self::Bytes(_) => Ok(self),
1129            Self::Number(_) => Ok(self),
1130            Self::Bool(_) => Ok(self),
1131            Self::String(_) => Ok(self),
1132            Self::Address(_) => Ok(self),
1133            Self::Hash(_) => Ok(self),
1134            Self::UtxoRefs(_) => Ok(self),
1135            Self::UtxoSet(_) => Ok(self),
1136        }
1137    }
1138}
1139
1140impl Composite for Output {
1141    fn components(&self) -> Vec<&Expression> {
1142        vec![&self.address, &self.datum, &self.amount]
1143    }
1144
1145    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1146    where
1147        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1148    {
1149        Ok(Self {
1150            address: f(self.address)?,
1151            datum: f(self.datum)?,
1152            amount: f(self.amount)?,
1153            optional: self.optional,
1154        })
1155    }
1156}
1157
1158impl Composite for Mint {
1159    fn components(&self) -> Vec<&Expression> {
1160        vec![&self.amount, &self.redeemer]
1161    }
1162
1163    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1164    where
1165        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1166    {
1167        Ok(Self {
1168            amount: f(self.amount)?,
1169            redeemer: f(self.redeemer)?,
1170        })
1171    }
1172}
1173
1174impl Composite for AdHocDirective {
1175    fn components(&self) -> Vec<&Expression> {
1176        self.data.values().collect()
1177    }
1178
1179    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1180    where
1181        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1182    {
1183        Ok(Self {
1184            name: self.name,
1185            data: self
1186                .data
1187                .into_iter()
1188                .map(|(k, v)| f(v).map(|v| (k, v)))
1189                .collect::<Result<_, _>>()?,
1190        })
1191    }
1192}
1193
1194impl Composite for CompilerOp {
1195    fn components(&self) -> Vec<&Expression> {
1196        match self {
1197            CompilerOp::BuildScriptAddress(x) => vec![x],
1198            CompilerOp::ComputeMinUtxo(x) => vec![x],
1199            CompilerOp::ComputeTipSlot => vec![],
1200            CompilerOp::ComputeSlotToTime(x) => vec![x],
1201            CompilerOp::ComputeTimeToSlot(x) => vec![x],
1202        }
1203    }
1204
1205    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1206    where
1207        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1208    {
1209        match self {
1210            CompilerOp::BuildScriptAddress(x) => Ok(CompilerOp::BuildScriptAddress(f(x)?)),
1211            CompilerOp::ComputeMinUtxo(x) => Ok(CompilerOp::ComputeMinUtxo(f(x)?)),
1212            CompilerOp::ComputeTipSlot => Ok(CompilerOp::ComputeTipSlot),
1213            CompilerOp::ComputeSlotToTime(x) => Ok(CompilerOp::ComputeSlotToTime(f(x)?)),
1214            CompilerOp::ComputeTimeToSlot(x) => Ok(CompilerOp::ComputeTimeToSlot(f(x)?)),
1215        }
1216    }
1217}
1218
1219impl Composite for Signers {
1220    fn components(&self) -> Vec<&Expression> {
1221        self.signers.iter().collect()
1222    }
1223
1224    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1225    where
1226        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1227    {
1228        Ok(Self {
1229            signers: self.signers.into_iter().map(f).collect::<Result<_, _>>()?,
1230        })
1231    }
1232}
1233
1234impl Composite for Collateral {
1235    fn components(&self) -> Vec<&Expression> {
1236        vec![&self.utxos]
1237    }
1238
1239    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1240    where
1241        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1242    {
1243        Ok(Self {
1244            utxos: f(self.utxos)?,
1245        })
1246    }
1247}
1248
1249impl Composite for Validity {
1250    fn components(&self) -> Vec<&Expression> {
1251        vec![&self.since, &self.until]
1252    }
1253
1254    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1255    where
1256        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1257    {
1258        Ok(Self {
1259            since: f(self.since)?,
1260            until: f(self.until)?,
1261        })
1262    }
1263}
1264
1265impl Composite for Metadata {
1266    fn components(&self) -> Vec<&Expression> {
1267        vec![&self.key, &self.value]
1268    }
1269
1270    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1271    where
1272        F: Fn(Expression) -> Result<Expression, Error> + Clone,
1273    {
1274        Ok(Self {
1275            key: f(self.key)?,
1276            value: f(self.value)?,
1277        })
1278    }
1279}
1280
1281impl Apply for Tx {
1282    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
1283        let tx = Tx {
1284            references: self.references.apply_args(args)?,
1285            inputs: self.inputs.apply_args(args)?,
1286            outputs: self.outputs.apply_args(args)?,
1287            validity: self.validity.apply_args(args)?,
1288            mints: self.mints.apply_args(args)?,
1289            burns: self.burns.apply_args(args)?,
1290            fees: self.fees.apply_args(args)?,
1291            adhoc: self.adhoc.apply_args(args)?,
1292            collateral: self.collateral.apply_args(args)?,
1293            signers: self.signers.apply_args(args)?,
1294            metadata: self.metadata.apply_args(args)?,
1295        };
1296
1297        Ok(tx)
1298    }
1299
1300    fn apply_inputs(self, args: &BTreeMap<String, UtxoSet>) -> Result<Self, Error> {
1301        Ok(Self {
1302            references: self.references.apply_inputs(args)?,
1303            inputs: self.inputs.apply_inputs(args)?,
1304            outputs: self.outputs.apply_inputs(args)?,
1305            validity: self.validity.apply_inputs(args)?,
1306            mints: self.mints.apply_inputs(args)?,
1307            burns: self.burns.apply_inputs(args)?,
1308            fees: self.fees.apply_inputs(args)?,
1309            adhoc: self.adhoc.apply_inputs(args)?,
1310            collateral: self.collateral.apply_inputs(args)?,
1311            signers: self.signers.apply_inputs(args)?,
1312            metadata: self.metadata.apply_inputs(args)?,
1313        })
1314    }
1315
1316    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
1317        Ok(Self {
1318            references: self.references.apply_fees(fees)?,
1319            inputs: self.inputs.apply_fees(fees)?,
1320            outputs: self.outputs.apply_fees(fees)?,
1321            validity: self.validity.apply_fees(fees)?,
1322            mints: self.mints.apply_fees(fees)?,
1323            burns: self.burns.apply_fees(fees)?,
1324            fees: self.fees.apply_fees(fees)?,
1325            adhoc: self.adhoc.apply_fees(fees)?,
1326            collateral: self.collateral.apply_fees(fees)?,
1327            signers: self.signers.apply_fees(fees)?,
1328            metadata: self.metadata.apply_fees(fees)?,
1329        })
1330    }
1331
1332    fn is_constant(&self) -> bool {
1333        self.inputs.iter().all(|x| x.is_constant())
1334            && self.outputs.iter().all(|x| x.is_constant())
1335            && self.mints.iter().all(|x| x.is_constant())
1336            && self.burns.iter().all(|x| x.is_constant())
1337            && self.fees.is_constant()
1338            && self.metadata.is_constant()
1339            && self.validity.is_constant()
1340            && self.references.is_constant()
1341            && self.collateral.is_constant()
1342            && self.adhoc.iter().all(|x| x.is_constant())
1343            && self.signers.is_constant()
1344    }
1345
1346    fn params(&self) -> BTreeMap<String, Type> {
1347        // TODO: analyze if necessary to add ref_inputs
1348        let mut params = BTreeMap::new();
1349        params.extend(self.inputs.params());
1350        params.extend(self.outputs.params());
1351        params.extend(self.mints.params());
1352        params.extend(self.burns.params());
1353        params.extend(self.fees.params());
1354        params.extend(self.adhoc.params());
1355        params.extend(self.signers.params());
1356        params.extend(self.validity.params());
1357        params.extend(self.metadata.params());
1358        params.extend(self.references.params());
1359        params.extend(self.collateral.params());
1360        params
1361    }
1362
1363    fn queries(&self) -> BTreeMap<String, InputQuery> {
1364        let mut queries = BTreeMap::new();
1365        queries.extend(self.inputs.queries());
1366        queries.extend(self.outputs.queries());
1367        queries.extend(self.mints.queries());
1368        queries.extend(self.burns.queries());
1369        queries.extend(self.fees.queries());
1370        queries.extend(self.adhoc.queries());
1371        queries.extend(self.signers.queries());
1372        queries.extend(self.validity.queries());
1373        queries.extend(self.metadata.queries());
1374        queries.extend(self.collateral.queries());
1375        queries.extend(self.references.queries());
1376        queries
1377    }
1378
1379    fn reduce(self) -> Result<Self, Error> {
1380        Ok(Self {
1381            references: self.references.reduce()?,
1382            inputs: self.inputs.reduce()?,
1383            outputs: self.outputs.reduce()?,
1384            validity: self.validity.reduce()?,
1385            mints: self.mints.reduce()?,
1386            burns: self.burns.reduce()?,
1387            fees: self.fees.reduce()?,
1388            adhoc: self.adhoc.reduce()?,
1389            collateral: self.collateral.reduce()?,
1390            signers: self.signers.reduce()?,
1391            metadata: self.metadata.reduce()?,
1392        })
1393    }
1394}
1395
1396#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1397pub enum ArgValue {
1398    Int(i128),
1399    Bool(bool),
1400    String(String),
1401    Bytes(Vec<u8>),
1402    Address(Vec<u8>),
1403    UtxoSet(UtxoSet),
1404    UtxoRef(UtxoRef),
1405}
1406
1407impl From<Vec<u8>> for ArgValue {
1408    fn from(value: Vec<u8>) -> Self {
1409        Self::Bytes(value)
1410    }
1411}
1412
1413impl From<String> for ArgValue {
1414    fn from(value: String) -> Self {
1415        Self::String(value)
1416    }
1417}
1418
1419impl From<&str> for ArgValue {
1420    fn from(value: &str) -> Self {
1421        Self::String(value.to_string())
1422    }
1423}
1424
1425impl From<bool> for ArgValue {
1426    fn from(value: bool) -> Self {
1427        Self::Bool(value)
1428    }
1429}
1430
1431macro_rules! impl_from_int_for_arg_value {
1432    ($($t:ty),*) => {
1433        $(
1434            impl From<$t> for ArgValue {
1435                fn from(value: $t) -> Self {
1436                    Self::Int(value as i128)
1437                }
1438            }
1439        )*
1440    };
1441}
1442
1443impl_from_int_for_arg_value!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
1444
1445pub type ArgMap = BTreeMap<String, ArgValue>;
1446
1447pub fn apply_args<T: Apply>(template: T, args: &ArgMap) -> Result<T, Error> {
1448    template.apply_args(args)
1449}
1450
1451pub fn apply_inputs<T: Apply>(template: T, args: &BTreeMap<String, UtxoSet>) -> Result<T, Error> {
1452    template.apply_inputs(args)
1453}
1454
1455pub fn apply_fees<T: Apply>(template: T, fees: u64) -> Result<T, Error> {
1456    template.apply_fees(fees)
1457}
1458
1459pub fn reduce<T: Apply>(template: T) -> Result<T, Error> {
1460    template.reduce()
1461}
1462
1463pub fn find_params<T: Apply>(template: &T) -> BTreeMap<String, Type> {
1464    template.params()
1465}
1466
1467pub fn find_queries<T: Apply>(template: &T) -> BTreeMap<String, InputQuery> {
1468    template.queries()
1469}
1470
1471#[cfg(test)]
1472mod tests {
1473    use super::*;
1474
1475    const SUBJECT_TIR: &str = include_str!("test_subject.tir");
1476
1477    fn get_subject_tx() -> Tx {
1478        serde_json::from_str::<Tx>(SUBJECT_TIR).unwrap()
1479    }
1480
1481    #[test]
1482    fn param_expression_is_applied() {
1483        let ir = Expression::EvalParam(Box::new(Param::ExpectValue("a".to_string(), Type::Int)));
1484
1485        let params = ir.params();
1486        assert_eq!(params.len(), 1);
1487        assert_eq!(params.get("a"), Some(&Type::Int));
1488
1489        let args = BTreeMap::from([("a".to_string(), ArgValue::Int(100))]);
1490
1491        let after = ir.apply_args(&args).unwrap();
1492
1493        assert_eq!(
1494            after,
1495            Expression::EvalParam(Box::new(Param::Set(Expression::Number(100),)))
1496        );
1497    }
1498
1499    #[test]
1500    fn nested_param_expression_is_applied() {
1501        let ir = Expression::EvalParam(Box::new(Param::ExpectInput(
1502            "out".to_string(),
1503            InputQuery {
1504                address: Expression::None,
1505                min_amount: Expression::None,
1506                r#ref: Expression::EvalParam(Box::new(Param::ExpectValue(
1507                    "in".to_string(),
1508                    Type::Int,
1509                ))),
1510                many: false,
1511                collateral: false,
1512            },
1513        )));
1514
1515        let params = ir.params();
1516        assert_eq!(params.len(), 1);
1517        assert_eq!(params.get("in"), Some(&Type::Int));
1518
1519        let args = BTreeMap::from([("in".to_string(), ArgValue::Int(100))]);
1520        let after = ir.apply_args(&args).unwrap();
1521
1522        let after = after.reduce().unwrap();
1523
1524        let queries = after.queries();
1525        assert_eq!(queries.len(), 1);
1526        assert_eq!(
1527            queries.get("out"),
1528            Some(&InputQuery {
1529                address: Expression::None,
1530                min_amount: Expression::None,
1531                r#ref: Expression::Number(100),
1532                many: false,
1533                collateral: false,
1534            })
1535        );
1536    }
1537
1538    #[test]
1539    fn param_expression_is_reduced() {
1540        let ir = Expression::EvalParam(Box::new(Param::Set(Expression::Number(3))));
1541
1542        let after = ir.reduce().unwrap();
1543
1544        assert_eq!(after, Expression::Number(3));
1545    }
1546
1547    #[test]
1548    fn test_apply_args() {
1549        let before = get_subject_tx();
1550
1551        let params = find_params(&before);
1552        assert_eq!(params.len(), 3);
1553        assert_eq!(params.get("sender"), Some(&Type::Address));
1554        assert_eq!(params.get("a"), Some(&Type::Int));
1555        assert_eq!(params.get("b"), Some(&Type::Int));
1556
1557        let args = BTreeMap::from([
1558            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1559            ("a".to_string(), ArgValue::Int(100)),
1560            ("b".to_string(), ArgValue::Int(200)),
1561        ]);
1562
1563        let after = apply_args(before, &args).unwrap();
1564
1565        let params = find_params(&after);
1566        assert_eq!(params.len(), 0);
1567    }
1568
1569    #[test]
1570    fn test_apply_inputs() {
1571        let before = get_subject_tx();
1572
1573        let args = BTreeMap::from([
1574            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1575            ("a".to_string(), ArgValue::Int(100)),
1576            ("b".to_string(), ArgValue::Int(200)),
1577        ]);
1578
1579        let before = apply_args(before, &args).unwrap();
1580
1581        let before = before.reduce().unwrap();
1582
1583        let queries = find_queries(&before);
1584        dbg!(&queries);
1585
1586        assert_eq!(queries.len(), 1);
1587        assert!(queries.contains_key("source"));
1588
1589        let inputs = BTreeMap::from([(
1590            "source".to_string(),
1591            UtxoSet::from([Utxo {
1592                r#ref: UtxoRef::new(b"abc", 0),
1593                address: b"abc".to_vec(),
1594                datum: None,
1595                assets: CanonicalAssets::from_naked_amount(300),
1596                script: None,
1597            }]),
1598        )]);
1599
1600        let after = apply_inputs(before, &inputs).unwrap();
1601
1602        let queries = find_queries(&after);
1603        dbg!(&queries);
1604
1605        assert_eq!(queries.len(), 0);
1606    }
1607
1608    #[test]
1609    fn test_apply_fees() {
1610        let before = get_subject_tx();
1611
1612        let args = BTreeMap::from([
1613            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1614            ("a".to_string(), ArgValue::Int(100)),
1615            ("b".to_string(), ArgValue::Int(200)),
1616        ]);
1617
1618        let before = apply_args(before, &args).unwrap().reduce().unwrap();
1619
1620        let after = before.apply_fees(100).unwrap().reduce().unwrap();
1621
1622        let queries = find_queries(&after);
1623
1624        let query = queries.get("source").unwrap();
1625
1626        assert_eq!(
1627            query,
1628            &InputQuery {
1629                address: Expression::Address(b"abc".to_vec()),
1630                min_amount: Expression::Assets(vec![AssetExpr {
1631                    policy: Expression::None,
1632                    asset_name: Expression::None,
1633                    amount: Expression::Number(400),
1634                }]),
1635                r#ref: Expression::None,
1636                many: false,
1637                collateral: false,
1638            }
1639        );
1640    }
1641
1642    #[test]
1643    fn built_in_expression_is_reduced() {
1644        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::NoOp(Expression::Number(5))));
1645
1646        let after = op.reduce().unwrap();
1647
1648        assert_eq!(after, Expression::Number(5))
1649    }
1650
1651    #[test]
1652    fn numeric_add_is_reduced() {
1653        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
1654            Expression::Number(1),
1655            Expression::Number(5),
1656        )));
1657
1658        let after = op.reduce().unwrap();
1659
1660        assert_eq!(after, Expression::Number(6));
1661    }
1662
1663    #[test]
1664    fn numeric_sub_is_reduced() {
1665        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Sub(
1666            Expression::Number(8),
1667            Expression::Number(5),
1668        )));
1669
1670        let after = op.reduce().unwrap();
1671
1672        assert_eq!(after, Expression::Number(3));
1673    }
1674
1675    #[test]
1676    fn nested_numeric_binary_op_is_reduced() {
1677        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
1678            Expression::Number(1),
1679            Expression::EvalBuiltIn(Box::new(BuiltInOp::Sub(
1680                Expression::Number(5),
1681                Expression::Number(3),
1682            ))),
1683        )));
1684
1685        let after = op.reduce().unwrap();
1686
1687        assert_eq!(after, Expression::Number(3));
1688    }
1689
1690    #[test]
1691    fn test_reduce_single_custom_asset_binary_op() {
1692        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
1693            Expression::Assets(vec![AssetExpr {
1694                policy: Expression::Bytes(b"abc".to_vec()),
1695                asset_name: Expression::Bytes(b"111".to_vec()),
1696                amount: Expression::Number(100),
1697            }]),
1698            Expression::Assets(vec![AssetExpr {
1699                policy: Expression::Bytes(b"abc".to_vec()),
1700                asset_name: Expression::Bytes(b"111".to_vec()),
1701                amount: Expression::Number(200),
1702            }]),
1703        )));
1704
1705        let reduced = op.reduce().unwrap();
1706
1707        match reduced {
1708            Expression::Assets(assets) => {
1709                assert_eq!(assets.len(), 1);
1710                assert_eq!(assets[0].policy, Expression::Bytes(b"abc".to_vec()));
1711                assert_eq!(assets[0].asset_name, Expression::Bytes(b"111".to_vec()));
1712                assert_eq!(assets[0].amount, Expression::Number(300));
1713            }
1714            _ => panic!("Expected assets"),
1715        };
1716    }
1717
1718    #[test]
1719    fn test_reduce_native_asset_binary_op() {
1720        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
1721            Expression::Assets(vec![AssetExpr {
1722                policy: Expression::None,
1723                asset_name: Expression::None,
1724                amount: Expression::Number(100),
1725            }]),
1726            Expression::Assets(vec![AssetExpr {
1727                policy: Expression::None,
1728                asset_name: Expression::None,
1729                amount: Expression::Number(200),
1730            }]),
1731        )));
1732
1733        let reduced = op.reduce().unwrap();
1734
1735        match reduced {
1736            Expression::Assets(assets) => {
1737                assert_eq!(assets.len(), 1);
1738                assert_eq!(assets[0].policy, Expression::None);
1739                assert_eq!(assets[0].asset_name, Expression::None);
1740                assert_eq!(assets[0].amount, Expression::Number(300));
1741            }
1742            _ => panic!("Expected assets"),
1743        };
1744    }
1745
1746    #[test]
1747    fn test_reduce_mixed_asset_binary_op() {
1748        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
1749            Expression::Assets(vec![AssetExpr {
1750                policy: Expression::None,
1751                asset_name: Expression::None,
1752                amount: Expression::Number(100),
1753            }]),
1754            Expression::Assets(vec![AssetExpr {
1755                policy: Expression::Bytes(b"abc".to_vec()),
1756                asset_name: Expression::Bytes(b"111".to_vec()),
1757                amount: Expression::Number(200),
1758            }]),
1759        )));
1760
1761        let reduced = op.reduce().unwrap();
1762
1763        match reduced {
1764            Expression::Assets(assets) => {
1765                assert_eq!(assets.len(), 2);
1766
1767                for asset in assets {
1768                    if asset.policy == Expression::None {
1769                        assert_eq!(asset.asset_name, Expression::None);
1770                        assert_eq!(asset.amount, Expression::Number(100));
1771                    } else {
1772                        assert_eq!(asset.policy, Expression::Bytes(b"abc".to_vec()));
1773                        assert_eq!(asset.asset_name, Expression::Bytes(b"111".to_vec()));
1774                        assert_eq!(asset.amount, Expression::Number(200));
1775                    }
1776                }
1777            }
1778            _ => panic!("Expected assets"),
1779        };
1780    }
1781
1782    #[test]
1783    fn test_reduce_coerce_noop() {
1784        let op = Expression::EvalCoerce(Box::new(Coerce::NoOp(Expression::Number(5))));
1785
1786        let reduced = op.reduce().unwrap();
1787
1788        match reduced {
1789            Expression::Number(5) => (),
1790            _ => panic!("Expected number 5"),
1791        };
1792    }
1793
1794    #[test]
1795    fn test_coerce_utxo_set_into_assets() {
1796        let utxos = vec![Utxo {
1797            r#ref: UtxoRef::new(b"abc", 1),
1798            address: b"abc".into(),
1799            datum: Some(Expression::Number(1)),
1800            assets: CanonicalAssets::from_defined_asset(b"abc", b"111", 1),
1801            script: None,
1802        }];
1803
1804        let op = Coerce::IntoAssets(Expression::UtxoSet(
1805            std::collections::HashSet::from_iter(utxos.clone().into_iter()).into(),
1806        ));
1807
1808        let reduced = op.reduce().unwrap();
1809
1810        assert_eq!(
1811            reduced,
1812            Coerce::NoOp(Expression::Assets(utxos[0].assets.clone().into()))
1813        );
1814    }
1815
1816    #[test]
1817    fn test_coerce_utxo_set_into_datum() {
1818        let utxos = vec![Utxo {
1819            r#ref: UtxoRef::new(b"abc", 1),
1820            address: b"abc".into(),
1821            datum: Some(Expression::Number(1)),
1822            assets: CanonicalAssets::from_naked_amount(1),
1823            script: None,
1824        }];
1825
1826        let op = Coerce::IntoDatum(Expression::UtxoSet(
1827            std::collections::HashSet::from_iter(utxos.clone().into_iter()).into(),
1828        ));
1829
1830        let reduced = op.reduce().unwrap();
1831
1832        assert_eq!(reduced, Coerce::NoOp(utxos[0].datum.clone().unwrap()));
1833    }
1834
1835    #[test]
1836    fn test_reduce_struct_property_access() {
1837        let object = Expression::Struct(StructExpr {
1838            constructor: 0,
1839            fields: vec![
1840                Expression::Number(1),
1841                Expression::Number(2),
1842                Expression::Number(3),
1843            ],
1844        });
1845
1846        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
1847            object.clone(),
1848            Expression::Number(1),
1849        )));
1850
1851        let reduced = op.reduce().unwrap();
1852
1853        assert_eq!(reduced, Expression::Number(2));
1854
1855        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
1856            object.clone(),
1857            Expression::Number(100),
1858        )));
1859
1860        let reduced = op.reduce();
1861
1862        match reduced {
1863            Err(Error::PropertyIndexNotFound(100, _)) => (),
1864            _ => panic!("Expected property index not found"),
1865        };
1866    }
1867
1868    #[test]
1869    fn test_reduce_list_property_access() {
1870        let object = Expression::List(vec![
1871            Expression::Number(1),
1872            Expression::Number(2),
1873            Expression::Number(3),
1874        ]);
1875
1876        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
1877            object.clone(),
1878            Expression::Number(1),
1879        )));
1880
1881        let reduced = op.reduce();
1882
1883        match reduced {
1884            Ok(Expression::Number(2)) => (),
1885            _ => panic!("Expected number 2"),
1886        };
1887
1888        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
1889            object.clone(),
1890            Expression::Number(100),
1891        )));
1892
1893        let reduced = op.reduce();
1894
1895        match reduced {
1896            Err(Error::PropertyIndexNotFound(100, _)) => (),
1897            _ => panic!("Expected property index not found"),
1898        };
1899    }
1900
1901    #[test]
1902    fn test_reduce_tuple_property_access() {
1903        let object = Expression::Tuple(Box::new((Expression::Number(1), Expression::Number(2))));
1904
1905        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
1906            object.clone(),
1907            Expression::Number(1),
1908        )));
1909
1910        let reduced = op.reduce();
1911
1912        match reduced {
1913            Ok(Expression::Number(2)) => (),
1914            _ => panic!("Expected number 2"),
1915        };
1916
1917        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
1918            object.clone(),
1919            Expression::Number(100),
1920        )));
1921
1922        let reduced = op.reduce();
1923
1924        match reduced {
1925            Err(Error::PropertyIndexNotFound(100, _)) => (),
1926            _ => panic!("Expected property index not found"),
1927        };
1928    }
1929
1930    #[test]
1931    fn test_string_concat_is_reduced() {
1932        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Concat(
1933            Expression::String("hello".to_string()),
1934            Expression::String("world".to_string()),
1935        )));
1936
1937        let reduced = op.reduce().unwrap();
1938
1939        match reduced {
1940            Expression::String(s) => assert_eq!(s, "helloworld"),
1941            _ => panic!("Expected string 'helloworld'"),
1942        }
1943    }
1944
1945    #[test]
1946    fn test_string_number_concat_is_reduced() {
1947        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Concat(
1948            Expression::String("hello".to_string()),
1949            Expression::Number(123),
1950        )));
1951
1952        let reduced = op.reduce().unwrap();
1953
1954        match reduced {
1955            Expression::String(s) => assert_eq!(s, "hello123"),
1956            _ => panic!("Expected string 'hello123'"),
1957        }
1958    }
1959
1960    #[test]
1961    fn test_bytes_concat_is_reduced() {
1962        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Concat(
1963            Expression::Bytes(vec![1, 2, 3]),
1964            Expression::Bytes(vec![4, 5, 6]),
1965        )));
1966
1967        let reduced = op.reduce().unwrap();
1968
1969        match reduced {
1970            Expression::Bytes(b) => assert_eq!(b, vec![1, 2, 3, 4, 5, 6]),
1971            _ => panic!("Expected bytes [1, 2, 3, 4, 5, 6]"),
1972        }
1973    }
1974
1975    #[test]
1976    fn test_list_concat_is_reduced() {
1977        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Concat(
1978            Expression::List(vec![Expression::Number(1)]),
1979            Expression::List(vec![Expression::Number(2)]),
1980        )));
1981
1982        let reduced = op.reduce().unwrap();
1983
1984        match reduced {
1985            Expression::List(b) => {
1986                assert_eq!(b, vec![Expression::Number(1), Expression::Number(2)])
1987            }
1988            _ => panic!("Expected List [Number(1), Number(2)"),
1989        }
1990    }
1991
1992    #[test]
1993    fn test_concat_type_mismatch_error() {
1994        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Concat(
1995            Expression::String("hello".to_string()),
1996            Expression::Bytes(vec![1, 2, 3]),
1997        )));
1998
1999        let reduced = op.reduce();
2000
2001        match reduced {
2002            Err(Error::InvalidBinaryOp(op, _, _)) => assert_eq!(op, "concat"),
2003            _ => panic!("Expected InvalidBinaryOp error"),
2004        }
2005    }
2006
2007    #[test]
2008    fn test_concat_with_none() {
2009        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Concat(
2010            Expression::String("hello".to_string()),
2011            Expression::None,
2012        )));
2013
2014        let reduced = op.reduce().unwrap();
2015
2016        match reduced {
2017            Expression::String(s) => assert_eq!(s, "hello"),
2018            _ => panic!("Expected string 'hello'"),
2019        }
2020    }
2021
2022    #[test]
2023    fn test_min_utxo_add_non_reduction() {
2024        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Add(
2025            Expression::Assets(vec![AssetExpr {
2026                policy: Expression::None,
2027                asset_name: Expression::None,
2028                amount: Expression::Number(29),
2029            }]),
2030            Expression::EvalCompiler(Box::new(CompilerOp::ComputeMinUtxo(Expression::Number(20)))),
2031        )));
2032
2033        let reduced = op.clone().reduce().unwrap();
2034
2035        assert!(op == reduced)
2036    }
2037
2038    #[test]
2039    fn test_min_utxo_sub_non_reduction() {
2040        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Sub(
2041            Expression::Assets(vec![AssetExpr {
2042                policy: Expression::None,
2043                asset_name: Expression::None,
2044                amount: Expression::Number(29),
2045            }]),
2046            Expression::EvalCompiler(Box::new(CompilerOp::ComputeMinUtxo(Expression::Number(20)))),
2047        )));
2048
2049        let reduced = op.clone().reduce().unwrap();
2050
2051        assert!(op == reduced)
2052    }
2053
2054    #[test]
2055    fn test_index_list_with_expression() {
2056        let list = Expression::List(vec![
2057            Expression::String("first".to_string()),
2058            Expression::String("second".to_string()),
2059            Expression::String("third".to_string()),
2060        ]);
2061
2062        let index_expr = Expression::Number(1);
2063        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(list.clone(), index_expr)));
2064
2065        let reduced = op.reduce().unwrap();
2066
2067        match reduced {
2068            Expression::String(s) => assert_eq!(s, "second"),
2069            _ => panic!("Expected string 'second'"),
2070        }
2071    }
2072
2073    #[test]
2074    fn test_index_list_out_of_bounds() {
2075        let list = Expression::List(vec![Expression::Number(1), Expression::Number(2)]);
2076
2077        let index_expr = Expression::Number(5);
2078        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(list.clone(), index_expr)));
2079
2080        let reduced = op.reduce();
2081
2082        match reduced {
2083            Err(Error::PropertyIndexNotFound(5, _)) => (),
2084            _ => panic!("Expected PropertyIndexNotFound error"),
2085        }
2086    }
2087
2088    #[test]
2089    fn test_index_tuple_with_expression() {
2090        let tuple = Expression::Tuple(Box::new((
2091            Expression::String("left".to_string()),
2092            Expression::String("right".to_string()),
2093        )));
2094
2095        let index_expr = Expression::Number(0);
2096        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(tuple.clone(), index_expr)));
2097
2098        let reduced = op.reduce().unwrap();
2099
2100        match reduced {
2101            Expression::String(s) => assert_eq!(s, "left"),
2102            _ => panic!("Expected string 'left'"),
2103        }
2104
2105        let index_expr = Expression::Number(1);
2106        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(tuple.clone(), index_expr)));
2107
2108        let reduced = op.reduce().unwrap();
2109
2110        match reduced {
2111            Expression::String(s) => assert_eq!(s, "right"),
2112            _ => panic!("Expected string 'right'"),
2113        }
2114    }
2115
2116    #[test]
2117    fn test_index_struct_with_expression() {
2118        let struct_expr = Expression::Struct(StructExpr {
2119            constructor: 0,
2120            fields: vec![
2121                Expression::String("field0".to_string()),
2122                Expression::String("field1".to_string()),
2123                Expression::String("field2".to_string()),
2124            ],
2125        });
2126
2127        let index_expr = Expression::Number(1);
2128        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2129            struct_expr.clone(),
2130            index_expr,
2131        )));
2132
2133        let reduced = op.reduce().unwrap();
2134
2135        match reduced {
2136            Expression::String(s) => assert_eq!(s, "field1"),
2137            _ => panic!("Expected string 'field1'"),
2138        }
2139    }
2140
2141    #[test]
2142    fn test_index_none_expression() {
2143        let none_expr = Expression::None;
2144        let index_expr = Expression::Number(0);
2145
2146        let op = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(none_expr, index_expr)));
2147
2148        let reduced = op.reduce();
2149
2150        match reduced {
2151            Err(Error::PropertyIndexNotFound(0, _)) => (),
2152            _ => panic!("Expected PropertyIndexNotFound error for None expression"),
2153        }
2154    }
2155
2156    #[test]
2157    fn test_indexable_trait_on_expression() {
2158        let list_expr = Expression::List(vec![Expression::Number(10), Expression::Number(20)]);
2159
2160        let result = list_expr.index(Expression::Number(0));
2161        assert_eq!(result, Some(Expression::Number(10)));
2162
2163        let result = list_expr.index(Expression::Number(1));
2164        assert_eq!(result, Some(Expression::Number(20)));
2165
2166        let result = list_expr.index(Expression::Number(2));
2167        assert_eq!(result, None);
2168
2169        let tuple_expr =
2170            Expression::Tuple(Box::new((Expression::Number(100), Expression::Number(200))));
2171
2172        let result = tuple_expr.index(Expression::Number(0));
2173        assert_eq!(result, Some(Expression::Number(100)));
2174
2175        let result = tuple_expr.index(Expression::Number(1));
2176        assert_eq!(result, Some(Expression::Number(200)));
2177
2178        let result = tuple_expr.index(Expression::Number(2));
2179        assert_eq!(result, None);
2180    }
2181
2182    #[test]
2183    fn test_nested_property_access() {
2184        let nested_expr = Expression::List(vec![
2185            Expression::Tuple(Box::new((Expression::Number(1), Expression::Number(2)))),
2186            Expression::Tuple(Box::new((Expression::Number(3), Expression::Number(4)))),
2187        ]);
2188
2189        // Access nested_expr[1][0] (should be 3)
2190        let first_access = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2191            nested_expr,
2192            Expression::Number(1),
2193        )));
2194
2195        let second_access = Expression::EvalBuiltIn(Box::new(BuiltInOp::Property(
2196            first_access,
2197            Expression::Number(0),
2198        )));
2199
2200        let reduced = second_access.reduce().unwrap();
2201
2202        match reduced {
2203            Expression::Number(n) => assert_eq!(n, 3),
2204            _ => panic!("Expected number 3"),
2205        }
2206    }
2207}