moverox_codegen/move_enum/
mod.rs

1use std::collections::HashMap;
2
3use quote::quote;
4use unsynn::{Ident, LiteralString, ToTokens as _, TokenStream};
5
6use crate::generics::GenericsExt;
7use crate::{named_fields, positional_fields};
8
9/// The full Rust struct declaration and its `new` constructor.
10pub(super) fn to_rust(
11    this: &move_syn::Enum,
12    thecrate: &TokenStream,
13    package: Option<&LiteralString>,
14    module: Option<&Ident>,
15    address_map: &HashMap<Ident, TokenStream>,
16) -> TokenStream {
17    let move_syn::Enum {
18        ident, generics, ..
19    } = this;
20
21    let extra_attrs: TokenStream = package
22        .into_iter()
23        .map(unsynn::ToTokens::to_token_stream)
24        .map(|addr| quote!(#[move_(address = #addr)]))
25        .chain(module.map(|ident| quote!(#[move_(module = #ident)])))
26        .collect();
27
28    let rust_generics = generics
29        .as_ref()
30        .map(GenericsExt::to_rust)
31        .unwrap_or_default();
32
33    // HACK: pipe potential phantom parameters into the first variant to become phantom data fields
34    let mut phantoms: Option<Vec<_>> = generics.as_ref().and_then(|generics| {
35        generics
36            .phantoms()
37            .next()
38            .is_some()
39            .then(|| generics.phantoms().cloned().collect())
40    });
41    let variants = this
42        .variants()
43        .map(|var| variant_to_rust(var, phantoms.take().as_deref(), address_map));
44
45    // NOTE: this has to be formatted as a string first, so that `quote!` will turn it into a
46    // string literal later, which is what `#[serde(crate = ...)]` accepts
47    let serde_crate = format!("{thecrate}::serde").replace(" ", "");
48    quote! {
49        #[derive(
50            Clone,
51            Debug,
52            PartialEq,
53            Eq,
54            Hash,
55            #thecrate::traits::MoveDatatype,
56            #thecrate::serde::Deserialize,
57            #thecrate::serde::Serialize,
58        )]
59        #[move_(crate = #thecrate::traits)]
60        #[serde(crate = #serde_crate)]
61        #extra_attrs
62        #[allow(non_snake_case)]
63        pub enum #ident #rust_generics {
64            #(#variants),*
65        }
66    }
67}
68
69fn variant_to_rust(
70    this: &move_syn::EnumVariant,
71    phantoms: Option<&[Ident]>,
72    address_map: &HashMap<Ident, TokenStream>,
73) -> TokenStream {
74    use move_syn::FieldsKind as K;
75    let move_syn::EnumVariant {
76        attrs,
77        ident,
78        fields,
79    } = this;
80    let attrs = attrs
81        .iter()
82        .filter(|attr| attr.is_doc())
83        .map(|attr| attr.to_token_stream());
84
85    // Move enum variants can have empty fields
86    let bool_if_empty = false;
87    // Public enums in Rust already have all their variants' fields public
88    let visibility = false;
89
90    // If the variant is a unit (empty) one but there are phantom parameters, make it into a
91    // positional fields one with just the phantom data
92    let default_fields = phantoms.map(|phantoms| {
93        positional_fields::to_rust(
94            &Default::default(),
95            phantoms.iter(),
96            address_map,
97            bool_if_empty,
98            visibility,
99        )
100    });
101
102    let fields = fields
103        .as_ref()
104        .map(|kind| match kind {
105            K::Named(named) => named_fields::to_rust(
106                named,
107                phantoms.into_iter().flatten(),
108                address_map,
109                visibility,
110            ),
111            K::Positional(positional) => positional_fields::to_rust(
112                positional,
113                phantoms.into_iter().flatten(),
114                address_map,
115                bool_if_empty,
116                visibility,
117            ),
118        })
119        .or(default_fields)
120        .unwrap_or_default();
121
122    quote! {
123        #(#attrs)*
124        #ident #fields
125    }
126}