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