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