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            // Withdrawing from a script stake credential runs its script.
134            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}