Skip to main content

tx3_tir/reduce/
mod.rs

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