Skip to main content

tx3_lang/
cardano.rs

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