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