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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}