moverox_codegen/move_enum/
mod.rs1use std::collections::{HashMap, HashSet};
2
3use move_syn::{FieldsKind, ItemPath};
4use quote::quote;
5use unsynn::{Ident, ToTokens as _, TokenStream};
6
7use crate::generics::GenericsExt;
8use crate::iter::BoxedIter as _;
9use crate::{ItemContext, Result, named_fields, positional_fields};
10
11pub(super) fn to_rust(
13 this: &move_syn::Enum,
14 otw_types: HashSet<Ident>,
15 ctx: ItemContext,
16) -> Result<TokenStream> {
17 let move_syn::Enum {
18 ident, generics, ..
19 } = this;
20
21 let extra_attrs: TokenStream = ctx
22 .package
23 .into_iter()
24 .map(unsynn::ToTokens::to_token_stream)
25 .map(|addr| quote!(#[move_(address = #addr)]))
26 .chain(ctx.module.map(|ident| quote!(#[move_(module = #ident)])))
27 .collect();
28
29 let type_generics = generics
30 .as_ref()
31 .map(|g| g.type_generics(ctx.thecrate, otw_types))
32 .transpose()
33 .map(Option::unwrap_or_default)?;
34
35 let mut phantoms = unused_phantoms(this);
36 let variants = this
37 .variants()
38 .map(|var| variant_to_rust(var, &std::mem::take(&mut phantoms), ctx.address_map));
40
41 let thecrate = ctx.thecrate;
44 let serde_crate = format!("{thecrate}::serde").replace(" ", "");
45 Ok(quote! {
46 #[derive(
47 Clone,
48 Debug,
49 PartialEq,
50 Eq,
51 Hash,
52 #thecrate::traits::MoveDatatype,
53 #thecrate::serde::Deserialize,
54 #thecrate::serde::Serialize,
55 )]
56 #[move_(crate = #thecrate::traits)]
57 #[serde(crate = #serde_crate)]
58 #extra_attrs
59 #[allow(non_snake_case)]
60 pub enum #ident #type_generics {
61 #(#variants),*
62 }
63 })
64}
65
66fn unused_phantoms(this: &move_syn::Enum) -> Vec<Ident> {
68 let Some(generics) = this.generics.as_ref() else {
69 return Vec::new(); };
71
72 let maybe_phantom_leaf_types: HashSet<_> = enum_leaf_types(this)
73 .filter_map(|path| match path {
74 ItemPath::Ident(ident) => Some(ident),
75 _ => None,
76 })
77 .collect();
78
79 generics
80 .phantoms()
81 .filter(|&ident| !maybe_phantom_leaf_types.contains(ident))
82 .cloned()
83 .collect()
84}
85
86fn enum_leaf_types(this: &move_syn::Enum) -> Box<dyn Iterator<Item = &ItemPath> + '_> {
90 this.variants()
91 .flat_map(|var| &var.fields)
92 .flat_map(|fields| match fields {
93 FieldsKind::Positional(positional) => {
94 leaf_types_recursive(positional.fields().map(|field| &field.ty).boxed())
95 }
96 FieldsKind::Named(named) => {
97 leaf_types_recursive(named.fields().map(|field| &field.ty).boxed())
98 }
99 })
100 .boxed()
101}
102
103fn leaf_types_recursive<'a>(
104 types: Box<dyn Iterator<Item = &'a move_syn::Type> + 'a>,
105) -> Box<dyn Iterator<Item = &'a ItemPath> + 'a> {
106 types
107 .into_iter()
108 .flat_map(|t| {
109 t.type_args.as_ref().map_or_else(
110 || std::iter::once(&t.path).boxed(),
111 |t_args| leaf_types_recursive(t_args.types().boxed()),
112 )
113 })
114 .boxed()
115}
116
117fn variant_to_rust(
118 this: &move_syn::EnumVariant,
119 phantoms: &[Ident],
120 address_map: &HashMap<Ident, TokenStream>,
121) -> TokenStream {
122 use move_syn::FieldsKind as K;
123 let move_syn::EnumVariant {
124 attrs,
125 ident,
126 fields,
127 } = this;
128 let attrs = attrs
129 .iter()
130 .filter(|attr| attr.is_doc())
131 .map(|attr| attr.to_token_stream());
132
133 let bool_if_empty = false;
135 let visibility = false;
137
138 let default_fields = (!phantoms.is_empty()).then(|| {
141 positional_fields::to_rust(
142 &Default::default(),
143 phantoms.iter(),
144 address_map,
145 bool_if_empty,
146 visibility,
147 )
148 });
149
150 let fields = fields
151 .as_ref()
152 .map(|kind| match kind {
153 K::Named(named) => {
154 named_fields::to_rust(named, phantoms.iter(), address_map, visibility)
155 }
156 K::Positional(positional) => positional_fields::to_rust(
157 positional,
158 phantoms.iter(),
159 address_map,
160 bool_if_empty,
161 visibility,
162 ),
163 })
164 .or(default_fields)
165 .unwrap_or_default();
166
167 quote! {
168 #(#attrs)*
169 #ident #fields
170 }
171}