moverox_codegen/move_struct/
mod.rs1use std::collections::{HashMap, HashSet};
2
3use move_syn::ItemPath;
4use proc_macro2::{Ident, TokenStream};
5use quote::quote;
6
7mod braced;
8mod tuple;
9
10use braced::BracedStructExt as _;
11use tuple::TupleStructExt as _;
12
13use crate::generics::GenericsExt;
14use crate::iter::BoxedIter as _;
15use crate::{ItemContext, Result};
16
17pub(super) trait StructGen {
18 fn to_rust(&self, otw_types: HashSet<Ident>, ctx: ItemContext<'_>) -> Result<TokenStream>;
21}
22
23impl StructGen for move_syn::Struct {
24 fn to_rust(&self, otw_types: HashSet<Ident>, ctx: ItemContext<'_>) -> Result<TokenStream> {
25 let decl = self.rust_declaration(otw_types, ctx)?;
26 let impl_new = self.impl_new(ctx.address_map);
27 let impl_has_key_maybe = self.impl_has_key(ctx.thecrate).unwrap_or_default();
28 Ok(quote! {
29 #decl
30 #impl_new
31 #impl_has_key_maybe
32 })
33 }
34}
35
36trait StructExt {
37 fn rust_declaration(
38 &self,
39 otw_types: HashSet<Ident>,
40 ctx: ItemContext<'_>,
41 ) -> Result<TokenStream>;
42
43 fn impl_new(&self, address_map: &HashMap<Ident, TokenStream>) -> TokenStream;
45
46 fn impl_has_key(&self, thecrate: &TokenStream) -> Option<TokenStream>;
49
50 fn extra_derives(&self) -> Option<TokenStream>;
55
56 fn generics(&self) -> TokenStream;
60
61 fn type_generics(
63 &self,
64 thecrate: &TokenStream,
65 otw_types: HashSet<Ident>,
66 ) -> Result<TokenStream>;
67
68 fn unused_phantoms(&self) -> impl Iterator<Item = &Ident>;
70}
71
72impl StructExt for move_syn::Struct {
73 fn rust_declaration(
74 &self,
75 otw_types: HashSet<Ident>,
76 ctx: ItemContext<'_>,
77 ) -> Result<TokenStream> {
78 use move_syn::StructKind as K;
79 let Self { ident, kind, .. } = self;
80
81 let extra_attrs: TokenStream = ctx
82 .package
83 .into_iter()
84 .map(unsynn::ToTokens::to_token_stream)
85 .map(|addr| quote!(#[move_(address = #addr)]))
86 .chain(ctx.module.map(|ident| quote!(#[move_(module = #ident)])))
87 .collect();
88 let extra_derives = self.extra_derives().unwrap_or_default();
89 let type_generics = self.type_generics(ctx.thecrate, otw_types)?;
90 let contents = match kind {
91 K::Braced(braced) => braced.to_rust_contents(self.unused_phantoms(), ctx.address_map),
92 K::Tuple(tuple) => tuple.to_rust_contents(self.unused_phantoms(), ctx.address_map),
93 };
94 let thecrate = ctx.thecrate;
97 let serde_crate = format!("{thecrate}::serde").replace(" ", "");
98 Ok(quote! {
99 #[derive(
100 #extra_derives
101 Clone,
102 Debug,
103 PartialEq,
104 Eq,
105 Hash,
106 #thecrate::traits::MoveDatatype,
107 #thecrate::serde::Deserialize,
108 #thecrate::serde::Serialize,
109 )]
110 #[move_(crate = #thecrate::traits)]
111 #[serde(crate = #serde_crate)]
112 #extra_attrs
113 #[allow(non_snake_case)]
114 pub struct #ident #type_generics #contents
115 })
116 }
117
118 fn impl_new(&self, address_map: &HashMap<Ident, TokenStream>) -> TokenStream {
119 use move_syn::StructKind;
120 let Self { ident, kind, .. } = self;
121 let generics = self.generics();
122 let (args, assignments) = match kind {
123 StructKind::Braced(braced) => braced.impl_new(self.unused_phantoms(), address_map),
124 StructKind::Tuple(tuple) => tuple.impl_new(self.unused_phantoms(), address_map),
125 };
126 quote! {
127 impl #generics #ident #generics {
128 #[allow(clippy::just_underscores_and_digits, clippy::too_many_arguments)]
129 pub const fn new(#args) -> Self {
130 Self #assignments
131 }
132 }
133 }
134 }
135
136 fn impl_has_key(&self, thecrate: &TokenStream) -> Option<TokenStream> {
147 use move_syn::Ability;
148 if !self.abilities().any(|a| matches!(a, Ability::Key(_))) {
149 return None;
150 }
151 let ident = &self.ident;
152 let generics = self.generics();
153 Some(quote! {
154 impl #generics #thecrate::traits::HasKey for #ident #generics {
155 fn address(&self) -> #thecrate::types::Address {
156 self.id.id.bytes
157 }
158 }
159 })
160 }
161
162 fn extra_derives(&self) -> Option<TokenStream> {
163 use move_syn::StructKind;
164 let is_empty = match &self.kind {
165 StructKind::Braced(braced) => braced.is_empty(),
166 StructKind::Tuple(tuple) => tuple.is_empty(),
167 };
168 is_empty.then_some(quote!(Default,))
169 }
170
171 fn generics(&self) -> TokenStream {
172 self.generics
173 .as_ref()
174 .map(GenericsExt::to_rust)
175 .unwrap_or_default()
176 }
177
178 fn type_generics(
179 &self,
180 thecrate: &TokenStream,
181 otw_types: HashSet<Ident>,
182 ) -> Result<TokenStream> {
183 self.generics
184 .as_ref()
185 .map(|g| g.type_generics(thecrate, otw_types))
186 .transpose()
187 .map(Option::unwrap_or_default)
188 }
189
190 fn unused_phantoms(&self) -> impl Iterator<Item = &Ident> {
192 unused_phantoms(self)
193 }
194}
195
196fn unused_phantoms(this: &move_syn::Struct) -> impl Iterator<Item = &Ident> {
198 let maybe_phantom_leaf_types: HashSet<_> = struct_leaf_types(this)
199 .filter_map(|path| match path {
200 ItemPath::Ident(ident) => Some(ident),
201 _ => None,
202 })
203 .collect();
204
205 this.generics
206 .iter()
207 .flat_map(|g| g.phantoms())
208 .filter(move |&ident| !maybe_phantom_leaf_types.contains(ident))
209}
210
211fn struct_leaf_types(this: &move_syn::Struct) -> Box<dyn Iterator<Item = &ItemPath> + '_> {
215 match &this.kind {
216 move_syn::StructKind::Braced(named) => {
217 leaf_types_recursive(named.fields().map(|field| &field.ty).boxed())
218 }
219 move_syn::StructKind::Tuple(positional) => {
220 leaf_types_recursive(positional.fields().map(|field| &field.ty).boxed())
221 }
222 }
223 .boxed()
224}
225
226fn leaf_types_recursive<'a>(
227 types: Box<dyn Iterator<Item = &'a move_syn::Type> + 'a>,
228) -> Box<dyn Iterator<Item = &'a ItemPath> + 'a> {
229 types
230 .into_iter()
231 .flat_map(|t| {
232 t.type_args.as_ref().map_or_else(
233 || std::iter::once(&t.path).boxed(),
234 |t_args| leaf_types_recursive(t_args.types().boxed()),
235 )
236 })
237 .boxed()
238}