tx3_lang/
ast.rs

1//! The Tx3 language abstract syntax tree (AST).
2//!
3//! This module defines the abstract syntax tree (AST) for the Tx3 language.
4//! It provides the structure for representing Tx3 programs, including
5//! transactions, types, assets, and other constructs.
6//!
7//! This module is not intended to be used directly by end-users. See
8//! [`parse_file`](crate::parse_file) and [`parse_string`](crate::parse_string)
9//! for parsing Tx3 source code into an AST.
10
11use serde::{Deserialize, Serialize};
12use std::{collections::HashMap, rc::Rc};
13
14#[derive(Debug, PartialEq, Eq)]
15pub struct Scope {
16    pub(crate) symbols: HashMap<String, Symbol>,
17    pub(crate) parent: Option<Rc<Scope>>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum Symbol {
22    EnvVar(String, Box<Type>),
23    ParamVar(String, Box<Type>),
24    LocalExpr(Box<DataExpr>),
25    Input(Box<InputBlock>),
26    PartyDef(Box<PartyDef>),
27    PolicyDef(Box<PolicyDef>),
28    AssetDef(Box<AssetDef>),
29    TypeDef(Box<TypeDef>),
30    RecordField(Box<RecordField>),
31    VariantCase(Box<VariantCase>),
32    Fees,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct Span {
37    dummy: bool,
38    pub start: usize,
39    pub end: usize,
40}
41
42impl Default for Span {
43    fn default() -> Self {
44        Self::DUMMY
45    }
46}
47
48impl Eq for Span {}
49
50impl PartialEq for Span {
51    fn eq(&self, other: &Self) -> bool {
52        if self.dummy || other.dummy {
53            return true;
54        }
55
56        self.start == other.start && self.end == other.end
57    }
58}
59
60impl std::hash::Hash for Span {
61    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
62        self.start.hash(state);
63        self.end.hash(state);
64    }
65}
66
67impl Span {
68    pub const DUMMY: Self = Self {
69        dummy: true,
70        start: 0,
71        end: 0,
72    };
73
74    pub fn new(start: usize, end: usize) -> Self {
75        Self {
76            dummy: false,
77            start,
78            end,
79        }
80    }
81}
82
83impl Symbol {
84    pub fn as_type_def(&self) -> Option<&TypeDef> {
85        match self {
86            Symbol::TypeDef(x) => Some(x.as_ref()),
87            _ => None,
88        }
89    }
90
91    pub fn as_variant_case(&self) -> Option<&VariantCase> {
92        match self {
93            Symbol::VariantCase(x) => Some(x.as_ref()),
94            _ => None,
95        }
96    }
97
98    pub fn as_field_def(&self) -> Option<&RecordField> {
99        match self {
100            Symbol::RecordField(x) => Some(x.as_ref()),
101            _ => None,
102        }
103    }
104
105    pub fn as_policy_def(&self) -> Option<&PolicyDef> {
106        match self {
107            Symbol::PolicyDef(x) => Some(x.as_ref()),
108            _ => None,
109        }
110    }
111
112    pub fn target_type(&self) -> Option<Type> {
113        match self {
114            Symbol::ParamVar(_, ty) => Some(ty.as_ref().clone()),
115            Symbol::RecordField(x) => Some(x.r#type.clone()),
116            Symbol::Input(x) => x.datum_is().cloned(),
117            x => {
118                dbg!(x);
119                None
120            }
121        }
122    }
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
126pub struct Identifier {
127    pub value: String,
128    pub span: Span,
129
130    // analysis
131    #[serde(skip)]
132    pub(crate) symbol: Option<Symbol>,
133}
134
135impl Identifier {
136    pub fn new(value: impl Into<String>) -> Self {
137        Self {
138            value: value.into(),
139            symbol: None,
140            span: Span::DUMMY,
141        }
142    }
143
144    pub fn try_symbol(&self) -> Result<&Symbol, crate::lowering::Error> {
145        match &self.symbol {
146            Some(symbol) => Ok(symbol),
147            None => Err(crate::lowering::Error::MissingAnalyzePhase(
148                self.value.clone(),
149            )),
150        }
151    }
152
153    pub fn target_type(&self) -> Option<Type> {
154        self.symbol.as_ref().and_then(|x| x.target_type())
155    }
156}
157
158impl AsRef<str> for Identifier {
159    fn as_ref(&self) -> &str {
160        &self.value
161    }
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
165pub struct Program {
166    pub env: Option<EnvDef>,
167    pub txs: Vec<TxDef>,
168    pub types: Vec<TypeDef>,
169    pub assets: Vec<AssetDef>,
170    pub parties: Vec<PartyDef>,
171    pub policies: Vec<PolicyDef>,
172    pub span: Span,
173
174    // analysis
175    #[serde(skip)]
176    pub(crate) scope: Option<Rc<Scope>>,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
180pub struct EnvField {
181    pub name: String,
182    pub r#type: Type,
183    pub span: Span,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
187pub struct EnvDef {
188    pub fields: Vec<EnvField>,
189    pub span: Span,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
193pub struct ParameterList {
194    pub parameters: Vec<ParamDef>,
195    pub span: Span,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
199pub struct TxDef {
200    pub name: Identifier,
201    pub parameters: ParameterList,
202    pub locals: Option<LocalsBlock>,
203    pub references: Vec<ReferenceBlock>,
204    pub inputs: Vec<InputBlock>,
205    pub outputs: Vec<OutputBlock>,
206    pub validity: Option<ValidityBlock>,
207    pub burn: Option<BurnBlock>,
208    pub mints: Vec<MintBlock>,
209    pub signers: Option<SignersBlock>,
210    pub adhoc: Vec<ChainSpecificBlock>,
211    pub span: Span,
212    pub collateral: Vec<CollateralBlock>,
213    pub metadata: Option<MetadataBlock>,
214
215    // analysis
216    #[serde(skip)]
217    pub(crate) scope: Option<Rc<Scope>>,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
221pub struct LocalsAssign {
222    pub name: Identifier,
223    pub value: DataExpr,
224    pub span: Span,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
228pub struct LocalsBlock {
229    pub assigns: Vec<LocalsAssign>,
230    pub span: Span,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
234pub struct StringLiteral {
235    pub value: String,
236    pub span: Span,
237}
238
239impl StringLiteral {
240    pub fn new(value: impl Into<String>) -> Self {
241        Self {
242            value: value.into(),
243            span: Span::DUMMY,
244        }
245    }
246}
247
248#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
249pub struct HexStringLiteral {
250    pub value: String,
251    pub span: Span,
252}
253
254impl HexStringLiteral {
255    pub fn new(value: impl Into<String>) -> Self {
256        Self {
257            value: value.into(),
258            span: Span::DUMMY,
259        }
260    }
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
264pub enum CollateralBlockField {
265    From(DataExpr),
266    MinAmount(DataExpr),
267    Ref(DataExpr),
268}
269
270impl CollateralBlockField {
271    fn key(&self) -> &str {
272        match self {
273            CollateralBlockField::From(_) => "from",
274            CollateralBlockField::MinAmount(_) => "min_amount",
275            CollateralBlockField::Ref(_) => "ref",
276        }
277    }
278
279    pub fn as_data_expr(&self) -> Option<&DataExpr> {
280        match self {
281            CollateralBlockField::Ref(x) => Some(x),
282            _ => None,
283        }
284    }
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
288pub struct CollateralBlock {
289    pub fields: Vec<CollateralBlockField>,
290    pub span: Span,
291}
292
293impl CollateralBlock {
294    pub(crate) fn find(&self, key: &str) -> Option<&CollateralBlockField> {
295        self.fields.iter().find(|x| x.key() == key)
296    }
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
300pub enum InputBlockField {
301    From(DataExpr),
302    DatumIs(Type),
303    MinAmount(DataExpr),
304    Redeemer(DataExpr),
305    Ref(DataExpr),
306}
307
308impl InputBlockField {
309    fn key(&self) -> &str {
310        match self {
311            InputBlockField::From(_) => "from",
312            InputBlockField::DatumIs(_) => "datum_is",
313            InputBlockField::MinAmount(_) => "min_amount",
314            InputBlockField::Redeemer(_) => "redeemer",
315            InputBlockField::Ref(_) => "ref",
316        }
317    }
318
319    pub fn as_data_expr(&self) -> Option<&DataExpr> {
320        match self {
321            InputBlockField::Redeemer(x) => Some(x),
322            InputBlockField::Ref(x) => Some(x),
323            _ => None,
324        }
325    }
326
327    pub fn as_datum_type(&self) -> Option<&Type> {
328        match self {
329            InputBlockField::DatumIs(x) => Some(x),
330            _ => None,
331        }
332    }
333}
334
335#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
336pub struct ReferenceBlock {
337    pub name: String,
338    pub r#ref: DataExpr,
339    pub span: Span,
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
343pub struct MetadataBlockField {
344    pub key: DataExpr,
345    pub value: DataExpr,
346    pub span: Span,
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
350pub struct MetadataBlock {
351    pub fields: Vec<MetadataBlockField>,
352    pub span: Span,
353}
354
355#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
356pub struct InputBlock {
357    pub name: String,
358    pub is_many: bool,
359    pub fields: Vec<InputBlockField>,
360    pub span: Span,
361}
362
363impl InputBlock {
364    pub(crate) fn find(&self, key: &str) -> Option<&InputBlockField> {
365        self.fields.iter().find(|x| x.key() == key)
366    }
367
368    pub(crate) fn datum_is(&self) -> Option<&Type> {
369        self.find("datum_is").and_then(|x| x.as_datum_type())
370    }
371}
372
373#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
374pub enum OutputBlockField {
375    To(Box<DataExpr>),
376    Amount(Box<DataExpr>),
377    Datum(Box<DataExpr>),
378}
379
380impl OutputBlockField {
381    fn key(&self) -> &str {
382        match self {
383            OutputBlockField::To(_) => "to",
384            OutputBlockField::Amount(_) => "amount",
385            OutputBlockField::Datum(_) => "datum",
386        }
387    }
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
391pub struct OutputBlock {
392    pub name: Option<String>,
393    pub fields: Vec<OutputBlockField>,
394    pub span: Span,
395}
396
397impl OutputBlock {
398    pub(crate) fn find(&self, key: &str) -> Option<&OutputBlockField> {
399        self.fields.iter().find(|x| x.key() == key)
400    }
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
404pub enum ValidityBlockField {
405    UntilSlot(Box<DataExpr>),
406    SinceSlot(Box<DataExpr>),
407}
408
409impl ValidityBlockField {
410    fn key(&self) -> &str {
411        match self {
412            ValidityBlockField::UntilSlot(_) => "until_slot",
413            ValidityBlockField::SinceSlot(_) => "since_slot",
414        }
415    }
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
419pub struct ValidityBlock {
420    pub fields: Vec<ValidityBlockField>,
421    pub span: Span,
422}
423
424impl ValidityBlock {
425    pub(crate) fn find(&self, key: &str) -> Option<&ValidityBlockField> {
426        self.fields.iter().find(|x| x.key() == key)
427    }
428}
429
430#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
431pub enum MintBlockField {
432    Amount(Box<DataExpr>),
433    Redeemer(Box<DataExpr>),
434}
435
436impl MintBlockField {
437    fn key(&self) -> &str {
438        match self {
439            MintBlockField::Amount(_) => "amount",
440            MintBlockField::Redeemer(_) => "redeemer",
441        }
442    }
443}
444
445#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
446pub struct MintBlock {
447    pub fields: Vec<MintBlockField>,
448    pub span: Span,
449}
450
451#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
452pub struct SignersBlock {
453    pub signers: Vec<DataExpr>,
454    pub span: Span,
455}
456
457impl MintBlock {
458    pub(crate) fn find(&self, key: &str) -> Option<&MintBlockField> {
459        self.fields.iter().find(|x| x.key() == key)
460    }
461}
462
463#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
464pub struct BurnBlock {
465    pub fields: Vec<MintBlockField>,
466    pub span: Span,
467}
468
469#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
470pub struct RecordField {
471    pub name: Identifier,
472    pub r#type: Type,
473    pub span: Span,
474}
475
476impl RecordField {
477    pub fn new(name: &str, r#type: Type) -> Self {
478        Self {
479            name: Identifier::new(name),
480            r#type,
481            span: Span::DUMMY,
482        }
483    }
484}
485
486#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
487pub struct PartyDef {
488    pub name: Identifier,
489    pub span: Span,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
493pub struct PartyField {
494    pub name: String,
495    pub party_type: String,
496}
497
498#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
499pub struct PolicyDef {
500    pub name: Identifier,
501    pub value: PolicyValue,
502    pub span: Span,
503}
504
505#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
506pub enum PolicyField {
507    Hash(DataExpr),
508    Script(DataExpr),
509    Ref(DataExpr),
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
513pub struct PolicyConstructor {
514    pub fields: Vec<PolicyField>,
515    pub span: Span,
516}
517
518impl PolicyConstructor {
519    pub(crate) fn find_field(&self, field: &str) -> Option<&PolicyField> {
520        self.fields.iter().find(|x| match x {
521            PolicyField::Hash(_) => field == "hash",
522            PolicyField::Script(_) => field == "script",
523            PolicyField::Ref(_) => field == "ref",
524        })
525    }
526}
527
528#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
529pub enum PolicyValue {
530    Constructor(PolicyConstructor),
531    Assign(HexStringLiteral),
532}
533
534#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
535pub struct StaticAssetConstructor {
536    pub r#type: Identifier,
537    pub amount: Box<DataExpr>,
538    pub span: Span,
539}
540
541impl StaticAssetConstructor {
542    pub fn target_type(&self) -> Option<Type> {
543        Some(Type::AnyAsset)
544    }
545}
546
547#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
548pub struct AnyAssetConstructor {
549    pub policy: Box<DataExpr>,
550    pub asset_name: Box<DataExpr>,
551    pub amount: Box<DataExpr>,
552    pub span: Span,
553}
554
555impl AnyAssetConstructor {
556    pub fn target_type(&self) -> Option<Type> {
557        Some(Type::AnyAsset)
558    }
559}
560
561#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
562pub struct RecordConstructorField {
563    pub name: Identifier,
564    pub value: Box<DataExpr>,
565    pub span: Span,
566}
567
568#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
569pub struct StructConstructor {
570    pub r#type: Identifier,
571    pub case: VariantCaseConstructor,
572    pub span: Span,
573
574    // analysis
575    #[serde(skip)]
576    pub scope: Option<Rc<Scope>>,
577}
578
579impl StructConstructor {
580    pub fn target_type(&self) -> Option<Type> {
581        self.r#type.symbol.as_ref().and_then(|x| x.target_type())
582    }
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
586pub struct VariantCaseConstructor {
587    pub name: Identifier,
588    pub fields: Vec<RecordConstructorField>,
589    pub spread: Option<Box<DataExpr>>,
590    pub span: Span,
591
592    // analysis
593    #[serde(skip)]
594    pub scope: Option<Rc<Scope>>,
595}
596
597impl VariantCaseConstructor {
598    pub fn find_field_value(&self, field: &str) -> Option<&DataExpr> {
599        self.fields
600            .iter()
601            .find(|x| x.name.value == field)
602            .map(|x| x.value.as_ref())
603    }
604}
605
606#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
607pub struct ListConstructor {
608    pub elements: Vec<DataExpr>,
609    pub span: Span,
610}
611
612impl ListConstructor {
613    pub fn target_type(&self) -> Option<Type> {
614        self.elements.first().and_then(|x| x.target_type())
615    }
616}
617
618#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
619pub struct UtxoRef {
620    pub txid: Vec<u8>,
621    pub index: u64,
622    pub span: Span,
623}
624
625#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
626pub struct NegateOp {
627    pub operand: Box<DataExpr>,
628    pub span: Span,
629}
630
631impl NegateOp {
632    pub fn target_type(&self) -> Option<Type> {
633        self.operand.target_type()
634    }
635}
636
637#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
638pub struct PropertyOp {
639    pub operand: Box<DataExpr>,
640    pub property: Box<Identifier>,
641    pub span: Span,
642
643    // analysis
644    #[serde(skip)]
645    pub(crate) scope: Option<Rc<Scope>>,
646}
647
648impl PropertyOp {
649    pub fn target_type(&self) -> Option<Type> {
650        self.property.target_type()
651    }
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
655pub struct AddOp {
656    pub lhs: Box<DataExpr>,
657    pub rhs: Box<DataExpr>,
658    pub span: Span,
659}
660
661impl AddOp {
662    pub fn target_type(&self) -> Option<Type> {
663        self.lhs.target_type()
664    }
665}
666
667#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
668pub struct SubOp {
669    pub lhs: Box<DataExpr>,
670    pub rhs: Box<DataExpr>,
671    pub span: Span,
672}
673
674impl SubOp {
675    pub fn target_type(&self) -> Option<Type> {
676        self.lhs.target_type()
677    }
678}
679
680#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
681pub enum DataExpr {
682    None,
683    Unit,
684    Number(i64),
685    Bool(bool),
686    String(StringLiteral),
687    HexString(HexStringLiteral),
688    StructConstructor(StructConstructor),
689    ListConstructor(ListConstructor),
690    StaticAssetConstructor(StaticAssetConstructor),
691    AnyAssetConstructor(AnyAssetConstructor),
692    Identifier(Identifier),
693    AddOp(AddOp),
694    SubOp(SubOp),
695    NegateOp(NegateOp),
696    PropertyOp(PropertyOp),
697    UtxoRef(UtxoRef),
698}
699
700impl DataExpr {
701    pub fn as_identifier(&self) -> Option<&Identifier> {
702        match self {
703            DataExpr::Identifier(x) => Some(x),
704            _ => None,
705        }
706    }
707
708    pub fn target_type(&self) -> Option<Type> {
709        match self {
710            DataExpr::Identifier(x) => x.target_type(),
711            DataExpr::None => Some(Type::Undefined),
712            DataExpr::Unit => Some(Type::Unit),
713            DataExpr::Number(_) => Some(Type::Int),
714            DataExpr::Bool(_) => Some(Type::Bool),
715            DataExpr::String(_) => Some(Type::Bytes),
716            DataExpr::HexString(_) => Some(Type::Bytes),
717            DataExpr::StructConstructor(x) => x.target_type(),
718            DataExpr::ListConstructor(x) => x.target_type(),
719            DataExpr::AddOp(x) => x.target_type(),
720            DataExpr::SubOp(x) => x.target_type(),
721            DataExpr::NegateOp(x) => x.target_type(),
722            DataExpr::PropertyOp(x) => x.target_type(),
723            DataExpr::StaticAssetConstructor(x) => x.target_type(),
724            DataExpr::AnyAssetConstructor(x) => x.target_type(),
725            DataExpr::UtxoRef(_) => Some(Type::UtxoRef),
726        }
727    }
728}
729
730#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
731pub enum AddressExpr {
732    String(StringLiteral),
733    HexString(HexStringLiteral),
734    Identifier(Identifier),
735}
736
737impl AddressExpr {
738    pub fn as_identifier(&self) -> Option<&Identifier> {
739        match self {
740            AddressExpr::Identifier(x) => Some(x),
741            _ => None,
742        }
743    }
744}
745
746#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
747pub enum Type {
748    Undefined,
749    Unit,
750    Int,
751    Bool,
752    Bytes,
753    Address,
754    Utxo,
755    UtxoRef,
756    AnyAsset,
757    List(Box<Type>),
758    Custom(Identifier),
759}
760
761impl std::fmt::Display for Type {
762    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
763        match self {
764            Type::Undefined => write!(f, "Undefined"),
765            Type::Unit => write!(f, "Unit"),
766            Type::Int => write!(f, "Int"),
767            Type::Bool => write!(f, "Bool"),
768            Type::Bytes => write!(f, "Bytes"),
769            Type::Address => write!(f, "Address"),
770            Type::UtxoRef => write!(f, "UtxoRef"),
771            Type::AnyAsset => write!(f, "AnyAsset"),
772            Type::Utxo => write!(f, "Utxo"),
773            Type::List(inner) => write!(f, "List<{}>", inner),
774            Type::Custom(id) => write!(f, "{}", id.value),
775        }
776    }
777}
778
779impl Type {
780    pub fn properties(&self) -> Vec<(String, Type)> {
781        match self {
782            Type::AnyAsset => {
783                vec![
784                    ("amount".to_string(), Type::Int),
785                    ("policy".to_string(), Type::Bytes),
786                    ("asset_name".to_string(), Type::Bytes),
787                ]
788            }
789            Type::UtxoRef => {
790                vec![
791                    ("tx_hash".to_string(), Type::Bytes),
792                    ("output_index".to_string(), Type::Int),
793                ]
794            }
795            Type::Custom(identifier) => {
796                let def = identifier.symbol.as_ref().and_then(|s| s.as_type_def());
797
798                match def {
799                    Some(ty) if ty.cases.len() == 1 => ty.cases[0]
800                        .fields
801                        .iter()
802                        .map(|f| (f.name.value.clone(), f.r#type.clone()))
803                        .collect(),
804                    _ => vec![],
805                }
806            }
807            _ => vec![],
808        }
809    }
810
811    pub fn property_index(&self, property: &str) -> Option<usize> {
812        let properties = Self::properties(self);
813        properties.iter().position(|(name, _)| name == property)
814    }
815}
816
817#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
818pub struct ParamDef {
819    pub name: Identifier,
820    pub r#type: Type,
821}
822
823#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
824pub struct TypeDef {
825    pub name: Identifier,
826    pub cases: Vec<VariantCase>,
827    pub span: Span,
828}
829
830impl TypeDef {
831    pub(crate) fn find_case_index(&self, case: &str) -> Option<usize> {
832        self.cases.iter().position(|x| x.name.value == case)
833    }
834
835    #[allow(dead_code)]
836    pub(crate) fn find_case(&self, case: &str) -> Option<&VariantCase> {
837        self.cases.iter().find(|x| x.name.value == case)
838    }
839}
840
841#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
842pub struct VariantCase {
843    pub name: Identifier,
844    pub fields: Vec<RecordField>,
845    pub span: Span,
846}
847
848impl VariantCase {
849    #[allow(dead_code)]
850    pub(crate) fn find_field_index(&self, field: &str) -> Option<usize> {
851        self.fields.iter().position(|x| x.name.value == field)
852    }
853
854    #[allow(dead_code)]
855    pub(crate) fn find_field(&self, field: &str) -> Option<&RecordField> {
856        self.fields.iter().find(|x| x.name.value == field)
857    }
858}
859
860#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
861pub struct AssetDef {
862    pub name: Identifier,
863    pub policy: DataExpr,
864    pub asset_name: DataExpr,
865    pub span: Span,
866}
867
868#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
869pub enum ChainSpecificBlock {
870    Cardano(crate::cardano::CardanoBlock),
871}