1use std::{collections::HashMap, rc::Rc};
2
3use pest::iterators::Pair;
4use serde::{Deserialize, Serialize};
5
6use tx3_tir::model::v1beta0 as ir;
7
8use crate::{
9 analyzing::{Analyzable, AnalyzeReport},
10 ast::{DataExpr, Identifier, Scope, Span, Type},
11 lowering::IntoLower,
12 parsing::{AstNode, Error, Rule},
13};
14
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
16pub enum WithdrawalField {
17 From(Box<DataExpr>),
18 Amount(Box<DataExpr>),
19 Redeemer(Box<DataExpr>),
20}
21
22impl WithdrawalField {
23 fn key(&self) -> &str {
24 match self {
25 WithdrawalField::From(_) => "from",
26 WithdrawalField::Amount(_) => "amount",
27 WithdrawalField::Redeemer(_) => "redeemer",
28 }
29 }
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
33pub struct WithdrawalBlock {
34 pub fields: Vec<WithdrawalField>,
35 pub span: Span,
36}
37
38impl WithdrawalBlock {
39 pub(crate) fn find(&self, key: &str) -> Option<&WithdrawalField> {
40 self.fields.iter().find(|x| x.key() == key)
41 }
42}
43
44impl AstNode for WithdrawalField {
45 const RULE: Rule = Rule::cardano_withdrawal_field;
46
47 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
48 match pair.as_rule() {
49 Rule::cardano_withdrawal_from => {
50 let pair = pair.into_inner().next().unwrap();
51 Ok(WithdrawalField::From(DataExpr::parse(pair)?.into()))
52 }
53 Rule::cardano_withdrawal_amount => {
54 let pair = pair.into_inner().next().unwrap();
55 Ok(WithdrawalField::Amount(DataExpr::parse(pair)?.into()))
56 }
57 Rule::cardano_withdrawal_redeemer => {
58 let pair = pair.into_inner().next().unwrap();
59 Ok(WithdrawalField::Redeemer(DataExpr::parse(pair)?.into()))
60 }
61 x => unreachable!("Unexpected rule in cardano_withdrawal_field: {:?}", x),
62 }
63 }
64
65 fn span(&self) -> &Span {
66 match self {
67 Self::From(x) => x.span(),
68 Self::Amount(x) => x.span(),
69 Self::Redeemer(x) => x.span(),
70 }
71 }
72}
73
74impl AstNode for WithdrawalBlock {
75 const RULE: Rule = Rule::cardano_withdrawal_block;
76
77 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
78 let span = pair.as_span().into();
79 let inner = pair.into_inner();
80
81 let fields = inner
82 .map(|x| WithdrawalField::parse(x))
83 .collect::<Result<Vec<_>, _>>()?;
84
85 Ok(WithdrawalBlock { fields, span })
86 }
87
88 fn span(&self) -> &Span {
89 &self.span
90 }
91}
92
93impl Analyzable for WithdrawalField {
94 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
95 match self {
96 WithdrawalField::From(x) => x.analyze(parent),
97 WithdrawalField::Amount(x) => {
98 let amount = x.analyze(parent.clone());
99 let amount_type = AnalyzeReport::expect_data_expr_type(x, &Type::Int);
100 amount + amount_type
101 }
102 WithdrawalField::Redeemer(x) => x.analyze(parent),
103 }
104 }
105
106 fn is_resolved(&self) -> bool {
107 match self {
108 WithdrawalField::From(x) => x.is_resolved(),
109 WithdrawalField::Amount(x) => x.is_resolved(),
110 WithdrawalField::Redeemer(x) => x.is_resolved(),
111 }
112 }
113}
114
115impl Analyzable for WithdrawalBlock {
116 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
117 self.fields.analyze(parent)
118 }
119
120 fn is_resolved(&self) -> bool {
121 self.fields.is_resolved()
122 }
123}
124
125impl IntoLower for WithdrawalField {
126 type Output = ir::Expression;
127
128 fn into_lower(
129 &self,
130 ctx: &crate::lowering::Context,
131 ) -> Result<Self::Output, crate::lowering::Error> {
132 match self {
133 WithdrawalField::From(x) => x.into_lower(&ctx.capturing_policy_refs()),
135 WithdrawalField::Amount(x) => x.into_lower(ctx),
136 WithdrawalField::Redeemer(x) => x.into_lower(ctx),
137 }
138 }
139}
140
141impl IntoLower for WithdrawalBlock {
142 type Output = ir::AdHocDirective;
143
144 fn into_lower(
145 &self,
146 ctx: &crate::lowering::Context,
147 ) -> Result<Self::Output, crate::lowering::Error> {
148 let credential = self
149 .find("from")
150 .ok_or_else(|| {
151 crate::lowering::Error::MissingRequiredField("from".to_string(), "WithdrawalBlock")
152 })?
153 .into_lower(ctx)?;
154
155 let amount = self
156 .find("amount")
157 .ok_or_else(|| {
158 crate::lowering::Error::MissingRequiredField(
159 "amount".to_string(),
160 "WithdrawalBlock",
161 )
162 })?
163 .into_lower(ctx)?;
164
165 let redeemer = self
166 .find("redeemer")
167 .map(|r| r.into_lower(ctx))
168 .transpose()?
169 .unwrap_or(ir::Expression::None);
170
171 Ok(ir::AdHocDirective {
172 name: "withdrawal".to_string(),
173 data: std::collections::HashMap::from([
174 ("credential".to_string(), credential),
175 ("amount".to_string(), amount),
176 ("redeemer".to_string(), redeemer),
177 ]),
178 })
179 }
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
183pub struct VoteDelegationCertificate {
184 pub drep: DataExpr,
185 pub stake: DataExpr,
186 pub span: Span,
187}
188
189impl AstNode for VoteDelegationCertificate {
190 const RULE: Rule = Rule::cardano_vote_delegation_certificate;
191
192 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
193 let span = pair.as_span().into();
194 let mut inner = pair.into_inner();
195
196 Ok(VoteDelegationCertificate {
197 drep: DataExpr::parse(inner.next().unwrap())?,
198 stake: DataExpr::parse(inner.next().unwrap())?,
199 span,
200 })
201 }
202
203 fn span(&self) -> &Span {
204 &self.span
205 }
206}
207
208impl Analyzable for VoteDelegationCertificate {
209 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
210 let drep = self.drep.analyze(parent.clone());
211 let stake = self.stake.analyze(parent.clone());
212
213 drep + stake
214 }
215
216 fn is_resolved(&self) -> bool {
217 self.drep.is_resolved() && self.stake.is_resolved()
218 }
219}
220
221impl IntoLower for VoteDelegationCertificate {
222 type Output = ir::AdHocDirective;
223
224 fn into_lower(
225 &self,
226 ctx: &crate::lowering::Context,
227 ) -> Result<Self::Output, crate::lowering::Error> {
228 Ok(ir::AdHocDirective {
229 name: "vote_delegation_certificate".to_string(),
230 data: HashMap::from([
231 ("drep".to_string(), self.drep.into_lower(ctx)?),
232 ("stake".to_string(), self.stake.into_lower(ctx)?),
233 ]),
234 })
235 }
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
239pub struct StakeDelegationCertificate {
240 pub pool: DataExpr,
241 pub stake: DataExpr,
242 pub span: Span,
243}
244
245impl AstNode for StakeDelegationCertificate {
246 const RULE: Rule = Rule::cardano_stake_delegation_certificate;
247
248 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
249 let span = pair.as_span().into();
250 let mut inner = pair.into_inner();
251
252 Ok(StakeDelegationCertificate {
253 pool: DataExpr::parse(inner.next().unwrap())?,
254 stake: DataExpr::parse(inner.next().unwrap())?,
255 span,
256 })
257 }
258
259 fn span(&self) -> &Span {
260 &self.span
261 }
262}
263
264impl Analyzable for StakeDelegationCertificate {
265 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
266 let pool = self.pool.analyze(parent.clone());
267 let stake = self.stake.analyze(parent.clone());
268
269 pool + stake
270 }
271
272 fn is_resolved(&self) -> bool {
273 self.pool.is_resolved() && self.stake.is_resolved()
274 }
275}
276
277impl IntoLower for StakeDelegationCertificate {
278 type Output = ir::AdHocDirective;
279
280 fn into_lower(
281 &self,
282 _ctx: &crate::lowering::Context,
283 ) -> Result<Self::Output, crate::lowering::Error> {
284 todo!("StakeDelegationCertificate lowering not implemented")
285 }
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
289pub enum PlutusWitnessField {
290 Version(DataExpr, Span),
291 Script(DataExpr, Span),
292}
293
294impl IntoLower for PlutusWitnessField {
295 type Output = (String, ir::Expression);
296
297 fn into_lower(
298 &self,
299 ctx: &crate::lowering::Context,
300 ) -> Result<Self::Output, crate::lowering::Error> {
301 match self {
302 PlutusWitnessField::Version(x, _) => Ok(("version".to_string(), x.into_lower(ctx)?)),
303 PlutusWitnessField::Script(x, _) => Ok(("script".to_string(), x.into_lower(ctx)?)),
304 }
305 }
306}
307
308impl AstNode for PlutusWitnessField {
309 const RULE: Rule = Rule::cardano_plutus_witness_field;
310
311 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
312 let span = pair.as_span().into();
313
314 match pair.as_rule() {
315 Rule::cardano_plutus_witness_version => {
316 Ok(PlutusWitnessField::Version(DataExpr::parse(pair)?, span))
317 }
318 Rule::cardano_plutus_witness_script => {
319 Ok(PlutusWitnessField::Script(DataExpr::parse(pair)?, span))
320 }
321 x => unreachable!("Unexpected rule in cardano_plutus_witness_field: {:?}", x),
322 }
323 }
324
325 fn span(&self) -> &Span {
326 match self {
327 PlutusWitnessField::Version(_, span) => span,
328 PlutusWitnessField::Script(_, span) => span,
329 }
330 }
331}
332
333impl Analyzable for PlutusWitnessField {
334 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
335 match self {
336 PlutusWitnessField::Version(x, _) => x.analyze(parent),
337 PlutusWitnessField::Script(x, _) => x.analyze(parent),
338 }
339 }
340
341 fn is_resolved(&self) -> bool {
342 match self {
343 PlutusWitnessField::Version(x, _) => x.is_resolved(),
344 PlutusWitnessField::Script(x, _) => x.is_resolved(),
345 }
346 }
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
350pub struct PlutusWitnessBlock {
351 pub fields: Vec<PlutusWitnessField>,
352 pub span: Span,
353}
354
355impl AstNode for PlutusWitnessBlock {
356 const RULE: Rule = Rule::cardano_plutus_witness_block;
357
358 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
359 let span = pair.as_span().into();
360 let inner = pair.into_inner();
361
362 let fields = inner
363 .map(|x| PlutusWitnessField::parse(x))
364 .collect::<Result<Vec<_>, _>>()?;
365
366 Ok(PlutusWitnessBlock { fields, span })
367 }
368
369 fn span(&self) -> &Span {
370 &self.span
371 }
372}
373
374impl Analyzable for PlutusWitnessBlock {
375 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
376 self.fields.analyze(parent)
377 }
378
379 fn is_resolved(&self) -> bool {
380 self.fields.is_resolved()
381 }
382}
383
384impl IntoLower for PlutusWitnessBlock {
385 type Output = ir::AdHocDirective;
386
387 fn into_lower(
388 &self,
389 ctx: &crate::lowering::Context,
390 ) -> Result<Self::Output, crate::lowering::Error> {
391 let data = self
392 .fields
393 .iter()
394 .map(|x| x.into_lower(ctx))
395 .collect::<Result<_, _>>()?;
396
397 Ok(ir::AdHocDirective {
398 name: "plutus_witness".to_string(),
399 data,
400 })
401 }
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
405pub enum NativeWitnessField {
406 Script(DataExpr, Span),
407}
408
409impl IntoLower for NativeWitnessField {
410 type Output = (String, ir::Expression);
411
412 fn into_lower(
413 &self,
414 ctx: &crate::lowering::Context,
415 ) -> Result<Self::Output, crate::lowering::Error> {
416 match self {
417 NativeWitnessField::Script(x, _) => Ok(("script".to_string(), x.into_lower(ctx)?)),
418 }
419 }
420}
421
422impl AstNode for NativeWitnessField {
423 const RULE: Rule = Rule::cardano_native_witness_field;
424
425 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
426 let span = pair.as_span().into();
427
428 match pair.as_rule() {
429 Rule::cardano_native_witness_script => {
430 Ok(NativeWitnessField::Script(DataExpr::parse(pair)?, span))
431 }
432 x => unreachable!("Unexpected rule in cardano_native_witness_field: {:?}", x),
433 }
434 }
435
436 fn span(&self) -> &Span {
437 match self {
438 NativeWitnessField::Script(_, span) => span,
439 }
440 }
441}
442
443impl Analyzable for NativeWitnessField {
444 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
445 match self {
446 NativeWitnessField::Script(x, _) => x.analyze(parent),
447 }
448 }
449
450 fn is_resolved(&self) -> bool {
451 match self {
452 NativeWitnessField::Script(x, _) => x.is_resolved(),
453 }
454 }
455}
456
457#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
458pub struct NativeWitnessBlock {
459 pub fields: Vec<NativeWitnessField>,
460 pub span: Span,
461}
462
463impl AstNode for NativeWitnessBlock {
464 const RULE: Rule = Rule::cardano_native_witness_block;
465
466 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
467 let span = pair.as_span().into();
468 let inner = pair.into_inner();
469
470 let fields = inner
471 .map(|x| NativeWitnessField::parse(x))
472 .collect::<Result<Vec<_>, _>>()?;
473
474 Ok(NativeWitnessBlock { fields, span })
475 }
476
477 fn span(&self) -> &Span {
478 &self.span
479 }
480}
481
482impl Analyzable for NativeWitnessBlock {
483 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
484 self.fields.analyze(parent)
485 }
486
487 fn is_resolved(&self) -> bool {
488 self.fields.is_resolved()
489 }
490}
491
492impl IntoLower for NativeWitnessBlock {
493 type Output = ir::AdHocDirective;
494
495 fn into_lower(
496 &self,
497 ctx: &crate::lowering::Context,
498 ) -> Result<Self::Output, crate::lowering::Error> {
499 let data = self
500 .fields
501 .iter()
502 .map(|x| x.into_lower(ctx))
503 .collect::<Result<_, _>>()?;
504
505 Ok(ir::AdHocDirective {
506 name: "native_witness".to_string(),
507 data,
508 })
509 }
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
513pub struct TreasuryDonationBlock {
514 pub coin: DataExpr,
515 pub span: Span,
516}
517
518impl AstNode for TreasuryDonationBlock {
519 const RULE: Rule = Rule::cardano_treasury_donation_block;
520
521 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
522 let span = pair.as_span().into();
523
524 let mut inner = pair.into_inner();
525 let coin = DataExpr::parse(inner.next().unwrap())?;
526
527 Ok(TreasuryDonationBlock { coin, span })
528 }
529
530 fn span(&self) -> &Span {
531 &self.span
532 }
533}
534
535impl Analyzable for TreasuryDonationBlock {
536 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
537 let coin = self.coin.analyze(parent);
538 let coin_type = AnalyzeReport::expect_data_expr_type(&self.coin, &Type::Int);
539
540 coin + coin_type
541 }
542
543 fn is_resolved(&self) -> bool {
544 self.coin.is_resolved()
545 }
546}
547
548impl IntoLower for TreasuryDonationBlock {
549 type Output = ir::AdHocDirective;
550
551 fn into_lower(
552 &self,
553 ctx: &crate::lowering::Context,
554 ) -> Result<Self::Output, crate::lowering::Error> {
555 let coin = self.coin.into_lower(ctx)?;
556
557 Ok(ir::AdHocDirective {
558 name: "treasury_donation".to_string(),
559 data: std::collections::HashMap::from([("coin".to_string(), coin)]),
560 })
561 }
562}
563
564#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
565pub enum CardanoPublishBlockField {
566 To(Box<DataExpr>),
567 Amount(Box<DataExpr>),
568 Datum(Box<DataExpr>),
569 Version(Box<DataExpr>),
570 Script(Box<DataExpr>),
571}
572
573impl CardanoPublishBlockField {
574 fn key(&self) -> &str {
575 match self {
576 CardanoPublishBlockField::To(_) => "to",
577 CardanoPublishBlockField::Amount(_) => "amount",
578 CardanoPublishBlockField::Datum(_) => "datum",
579 CardanoPublishBlockField::Version(_) => "version",
580 CardanoPublishBlockField::Script(_) => "script",
581 }
582 }
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
586pub struct CardanoPublishBlock {
587 pub name: Option<Identifier>,
588 pub fields: Vec<CardanoPublishBlockField>,
589 pub span: Span,
590}
591
592impl CardanoPublishBlock {
593 pub(crate) fn find(&self, key: &str) -> Option<&CardanoPublishBlockField> {
594 self.fields.iter().find(|x| x.key() == key)
595 }
596}
597
598impl AstNode for CardanoPublishBlockField {
599 const RULE: Rule = Rule::cardano_publish_block_field;
600
601 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
602 match pair.as_rule() {
603 Rule::cardano_publish_block_to => {
604 let pair = pair.into_inner().next().unwrap();
605 Ok(CardanoPublishBlockField::To(DataExpr::parse(pair)?.into()))
606 }
607 Rule::cardano_publish_block_amount => {
608 let pair = pair.into_inner().next().unwrap();
609 Ok(CardanoPublishBlockField::Amount(
610 DataExpr::parse(pair)?.into(),
611 ))
612 }
613 Rule::cardano_publish_block_datum => {
614 let pair = pair.into_inner().next().unwrap();
615 Ok(CardanoPublishBlockField::Datum(
616 DataExpr::parse(pair)?.into(),
617 ))
618 }
619 Rule::cardano_publish_block_version => {
620 let pair = pair.into_inner().next().unwrap();
621 Ok(CardanoPublishBlockField::Version(
622 DataExpr::parse(pair)?.into(),
623 ))
624 }
625 Rule::cardano_publish_block_script => {
626 let pair = pair.into_inner().next().unwrap();
627 Ok(CardanoPublishBlockField::Script(
628 DataExpr::parse(pair)?.into(),
629 ))
630 }
631 x => unreachable!("Unexpected rule in cardano_publish_block_field: {:?}", x),
632 }
633 }
634
635 fn span(&self) -> &Span {
636 match self {
637 Self::To(x) => x.span(),
638 Self::Amount(x) => x.span(),
639 Self::Datum(x) => x.span(),
640 Self::Version(x) => x.span(),
641 Self::Script(x) => x.span(),
642 }
643 }
644}
645
646impl AstNode for CardanoPublishBlock {
647 const RULE: Rule = Rule::cardano_publish_block;
648
649 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
650 let span = pair.as_span().into();
651 let mut inner = pair.into_inner();
652 let has_name = inner
653 .peek()
654 .map(|x| x.as_rule() == Rule::identifier)
655 .unwrap_or_default();
656
657 let name = if has_name {
658 Some(Identifier::parse(inner.next().unwrap())?)
659 } else {
660 None
661 };
662
663 let fields = inner
664 .map(|x| CardanoPublishBlockField::parse(x))
665 .collect::<Result<Vec<_>, _>>()?;
666
667 Ok(CardanoPublishBlock { name, fields, span })
668 }
669
670 fn span(&self) -> &Span {
671 &self.span
672 }
673}
674
675impl Analyzable for CardanoPublishBlockField {
676 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
677 match self {
678 CardanoPublishBlockField::To(x) => x.analyze(parent),
679 CardanoPublishBlockField::Amount(x) => x.analyze(parent),
680 CardanoPublishBlockField::Datum(x) => x.analyze(parent),
681 CardanoPublishBlockField::Version(x) => x.analyze(parent),
682 CardanoPublishBlockField::Script(x) => x.analyze(parent),
683 }
684 }
685
686 fn is_resolved(&self) -> bool {
687 match self {
688 CardanoPublishBlockField::To(x) => x.is_resolved(),
689 CardanoPublishBlockField::Amount(x) => x.is_resolved(),
690 CardanoPublishBlockField::Datum(x) => x.is_resolved(),
691 CardanoPublishBlockField::Version(x) => x.is_resolved(),
692 CardanoPublishBlockField::Script(x) => x.is_resolved(),
693 }
694 }
695}
696
697impl Analyzable for CardanoPublishBlock {
698 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
699 self.fields.analyze(parent)
700 }
701
702 fn is_resolved(&self) -> bool {
703 self.fields.is_resolved()
704 }
705}
706
707impl IntoLower for CardanoPublishBlockField {
708 type Output = (String, ir::Expression);
709
710 fn into_lower(
711 &self,
712 ctx: &crate::lowering::Context,
713 ) -> Result<Self::Output, crate::lowering::Error> {
714 match self {
715 CardanoPublishBlockField::To(x) => {
716 let ctx = ctx.enter_address_expr();
717 Ok(("to".to_string(), x.into_lower(&ctx)?))
718 }
719 CardanoPublishBlockField::Amount(x) => {
720 let ctx = ctx.enter_asset_expr();
721 Ok(("amount".to_string(), x.into_lower(&ctx)?))
722 }
723 CardanoPublishBlockField::Datum(x) => {
724 let ctx = ctx.enter_datum_expr();
725 Ok(("datum".to_string(), x.into_lower(&ctx)?))
726 }
727 CardanoPublishBlockField::Version(x) => Ok(("version".to_string(), x.into_lower(ctx)?)),
728 CardanoPublishBlockField::Script(x) => Ok(("script".to_string(), x.into_lower(ctx)?)),
729 }
730 }
731}
732
733impl IntoLower for CardanoPublishBlock {
734 type Output = ir::AdHocDirective;
735
736 fn into_lower(
737 &self,
738 ctx: &crate::lowering::Context,
739 ) -> Result<Self::Output, crate::lowering::Error> {
740 let data = self
741 .fields
742 .iter()
743 .map(|x| x.into_lower(ctx))
744 .collect::<Result<_, _>>()?;
745
746 Ok(ir::AdHocDirective {
747 name: "cardano_publish".to_string(),
748 data,
749 })
750 }
751}
752
753#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
754pub enum CardanoBlock {
755 VoteDelegationCertificate(VoteDelegationCertificate),
756 StakeDelegationCertificate(StakeDelegationCertificate),
757 Withdrawal(WithdrawalBlock),
758 PlutusWitness(PlutusWitnessBlock),
759 NativeWitness(NativeWitnessBlock),
760 TreasuryDonation(TreasuryDonationBlock),
761 Publish(CardanoPublishBlock),
762}
763
764impl AstNode for CardanoBlock {
765 const RULE: Rule = Rule::cardano_block;
766
767 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
768 let mut inner = pair.into_inner();
769 let item = inner.next().unwrap();
770
771 match item.as_rule() {
772 Rule::cardano_vote_delegation_certificate => Ok(
773 CardanoBlock::VoteDelegationCertificate(VoteDelegationCertificate::parse(item)?),
774 ),
775 Rule::cardano_stake_delegation_certificate => Ok(
776 CardanoBlock::StakeDelegationCertificate(StakeDelegationCertificate::parse(item)?),
777 ),
778 Rule::cardano_withdrawal_block => {
779 Ok(CardanoBlock::Withdrawal(WithdrawalBlock::parse(item)?))
780 }
781 Rule::cardano_plutus_witness_block => Ok(CardanoBlock::PlutusWitness(
782 PlutusWitnessBlock::parse(item)?,
783 )),
784 Rule::cardano_native_witness_block => Ok(CardanoBlock::NativeWitness(
785 NativeWitnessBlock::parse(item)?,
786 )),
787 Rule::cardano_treasury_donation_block => Ok(CardanoBlock::TreasuryDonation(
788 TreasuryDonationBlock::parse(item)?,
789 )),
790 Rule::cardano_publish_block => {
791 Ok(CardanoBlock::Publish(CardanoPublishBlock::parse(item)?))
792 }
793 x => unreachable!("Unexpected rule in cardano_block: {:?}", x),
794 }
795 }
796
797 fn span(&self) -> &Span {
798 match self {
799 CardanoBlock::VoteDelegationCertificate(x) => x.span(),
800 CardanoBlock::StakeDelegationCertificate(x) => x.span(),
801 CardanoBlock::Withdrawal(x) => x.span(),
802 CardanoBlock::PlutusWitness(x) => x.span(),
803 CardanoBlock::NativeWitness(x) => x.span(),
804 CardanoBlock::TreasuryDonation(x) => x.span(),
805 CardanoBlock::Publish(x) => x.span(),
806 }
807 }
808}
809
810impl Analyzable for CardanoBlock {
811 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
812 match self {
813 CardanoBlock::VoteDelegationCertificate(x) => x.analyze(parent),
814 CardanoBlock::StakeDelegationCertificate(x) => x.analyze(parent),
815 CardanoBlock::Withdrawal(x) => x.analyze(parent),
816 CardanoBlock::PlutusWitness(x) => x.analyze(parent),
817 CardanoBlock::NativeWitness(x) => x.analyze(parent),
818 CardanoBlock::TreasuryDonation(x) => x.analyze(parent),
819 CardanoBlock::Publish(x) => x.analyze(parent),
820 }
821 }
822
823 fn is_resolved(&self) -> bool {
824 match self {
825 CardanoBlock::VoteDelegationCertificate(x) => x.is_resolved(),
826 CardanoBlock::StakeDelegationCertificate(x) => x.is_resolved(),
827 CardanoBlock::Withdrawal(x) => x.is_resolved(),
828 CardanoBlock::PlutusWitness(x) => x.is_resolved(),
829 CardanoBlock::NativeWitness(x) => x.is_resolved(),
830 Self::TreasuryDonation(x) => x.is_resolved(),
831 CardanoBlock::Publish(x) => x.is_resolved(),
832 }
833 }
834}
835
836impl IntoLower for CardanoBlock {
837 type Output = ir::AdHocDirective;
838
839 fn into_lower(
840 &self,
841 ctx: &crate::lowering::Context,
842 ) -> Result<<CardanoBlock as IntoLower>::Output, crate::lowering::Error> {
843 match self {
844 CardanoBlock::VoteDelegationCertificate(x) => x.into_lower(ctx),
845 CardanoBlock::StakeDelegationCertificate(x) => x.into_lower(ctx),
846 CardanoBlock::Withdrawal(x) => x.into_lower(ctx),
847 CardanoBlock::PlutusWitness(x) => x.into_lower(ctx),
848 CardanoBlock::NativeWitness(x) => x.into_lower(ctx),
849 CardanoBlock::TreasuryDonation(x) => x.into_lower(ctx),
850 CardanoBlock::Publish(x) => x.into_lower(ctx),
851 }
852 }
853}
854
855#[cfg(test)]
856mod tests {
857 use super::*;
858 use crate::{
859 analyzing::analyze,
860 ast::{self, *},
861 };
862 use pest::Parser;
863
864 macro_rules! input_to_ast_check {
865 ($ast:ty, $name:expr, $input:expr, $expected:expr) => {
866 paste::paste! {
867 #[test]
868 fn [<test_parse_ $ast:snake _ $name>]() {
869 let pairs = crate::parsing::Tx3Grammar::parse(<$ast>::RULE, $input).unwrap();
870 let single_match = pairs.into_iter().next().unwrap();
871 let result = <$ast>::parse(single_match).unwrap();
872
873 assert_eq!(result, $expected);
874 }
875 }
876 };
877 }
878
879 input_to_ast_check!(
880 PlutusWitnessBlock,
881 "basic",
882 "plutus_witness {
883 version: 3,
884 script: 0xABCDEF,
885 }",
886 PlutusWitnessBlock {
887 fields: vec![
888 PlutusWitnessField::Version(DataExpr::Number(3), Span::DUMMY),
889 PlutusWitnessField::Script(
890 DataExpr::HexString(HexStringLiteral::new("ABCDEF".to_string())),
891 Span::DUMMY
892 )
893 ],
894 span: Span::DUMMY,
895 }
896 );
897
898 input_to_ast_check!(
899 NativeWitnessBlock,
900 "basic",
901 "native_witness {
902 script: 0xABCDEF,
903 }",
904 NativeWitnessBlock {
905 fields: vec![NativeWitnessField::Script(
906 DataExpr::HexString(HexStringLiteral::new("ABCDEF".to_string())),
907 Span::DUMMY
908 )],
909 span: Span::DUMMY,
910 }
911 );
912
913 input_to_ast_check!(
914 TreasuryDonationBlock,
915 "basic",
916 "treasury_donation {
917 coin: 2020,
918 }",
919 TreasuryDonationBlock {
920 coin: DataExpr::Number(2020),
921 span: Span::DUMMY,
922 }
923 );
924
925 input_to_ast_check!(
926 CardanoPublishBlock,
927 "basic",
928 "publish {
929 to: Receiver,
930 amount: Ada(quantity),
931 version: 3,
932 script: 0xABCDEF,
933 }",
934 CardanoPublishBlock {
935 name: None,
936 fields: vec![
937 CardanoPublishBlockField::To(Box::new(DataExpr::Identifier(Identifier::new(
938 "Receiver"
939 )))),
940 CardanoPublishBlockField::Amount(Box::new(DataExpr::FnCall(crate::ast::FnCall {
941 callee: Identifier::new("Ada"),
942 args: vec![DataExpr::Identifier(Identifier::new("quantity"))],
943 span: Span::DUMMY,
944 }))),
945 CardanoPublishBlockField::Version(Box::new(DataExpr::Number(3))),
946 CardanoPublishBlockField::Script(Box::new(DataExpr::HexString(
947 HexStringLiteral::new("ABCDEF".to_string())
948 ))),
949 ],
950 span: Span::DUMMY,
951 }
952 );
953
954 input_to_ast_check!(
955 CardanoPublishBlock,
956 "basic_with_name",
957 "publish test_publish {
958 to: Receiver,
959 amount: Ada(quantity),
960 version: 3,
961 script: 0xABCDEF,
962 }",
963 CardanoPublishBlock {
964 name: Some(Identifier::new("test_publish")),
965 fields: vec![
966 CardanoPublishBlockField::To(Box::new(DataExpr::Identifier(Identifier::new(
967 "Receiver"
968 )))),
969 CardanoPublishBlockField::Amount(Box::new(DataExpr::FnCall(crate::ast::FnCall {
970 callee: Identifier::new("Ada"),
971 args: vec![DataExpr::Identifier(Identifier::new("quantity"))],
972 span: Span::DUMMY,
973 }))),
974 CardanoPublishBlockField::Version(Box::new(DataExpr::Number(3))),
975 CardanoPublishBlockField::Script(Box::new(DataExpr::HexString(
976 HexStringLiteral::new("ABCDEF".to_string())
977 ))),
978 ],
979 span: Span::DUMMY,
980 }
981 );
982
983 #[test]
984 fn test_treasury_donation_type() {
985 let mut ast = crate::parsing::parse_string(
986 r#"
987 tx test(quantity: Int) {
988 cardano::treasury_donation {
989 coin: quantity,
990 }
991 }
992 "#,
993 )
994 .unwrap();
995
996 let result = analyze(&mut ast);
997 assert!(result.errors.is_empty());
998 }
999
1000 #[test]
1001 fn test_treasury_donation_type_not_ok() {
1002 let mut ast = crate::parsing::parse_string(
1003 r#"
1004 tx test(quantity: Bytes) {
1005 cardano::treasury_donation {
1006 coin: quantity,
1007 }
1008 }
1009 "#,
1010 )
1011 .unwrap();
1012
1013 let result = analyze(&mut ast);
1014 assert!(!result.errors.is_empty());
1015 }
1016
1017 #[test]
1018 fn test_publish_type_ok() {
1019 let mut ast = crate::parsing::parse_string(
1020 r#"
1021 party Receiver;
1022
1023 tx test(quantity: Int) {
1024 cardano::publish {
1025 to: Receiver,
1026 amount: Ada(quantity),
1027 version: 3,
1028 script: 0xABCDEF,
1029 }
1030 }
1031 "#,
1032 )
1033 .unwrap();
1034
1035 let result = analyze(&mut ast);
1036 assert!(result.errors.is_empty());
1037 }
1038
1039 #[test]
1040 fn test_publish_type_with_name_ok() {
1041 let mut ast = crate::parsing::parse_string(
1042 r#"
1043 party Receiver;
1044
1045 tx test(quantity: Int) {
1046 cardano::publish deploy {
1047 to: Receiver,
1048 amount: Ada(quantity),
1049 version: 3,
1050 script: 0xABCDEF,
1051 }
1052 }
1053 "#,
1054 )
1055 .unwrap();
1056
1057 let result = analyze(&mut ast);
1058 assert!(result.errors.is_empty());
1059 }
1060}