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