1#![cfg_attr(nightly, feature(doc_cfg))]
2
3use std::collections::HashMap;
13
14use move_syn::{Attributes, Item, Module};
15use proc_macro2::{Ident, TokenStream};
16use quote::quote;
17use unsynn::LiteralString;
18
19mod attributes;
20mod generics;
21mod iter;
22mod move_enum;
23mod move_struct;
24mod move_type;
25mod named_fields;
26mod positional_fields;
27#[cfg(test)]
28mod tests;
29
30use self::move_struct::StructGen as _;
31
32type BoxError = Box<dyn std::error::Error + 'static>;
33type Result<T = (), E = BoxError> = std::result::Result<T, E>;
34
35pub struct ModuleParts {
41 pub ident: Ident,
43 pub docs: TokenStream,
45 pub body: TokenStream,
48}
49
50#[sealed::sealed]
51pub trait ModuleGen {
52 fn to_rust(
54 &self,
55 thecrate: &TokenStream,
56 package: Option<&LiteralString>,
57 address_map: &HashMap<Ident, TokenStream>,
58 ) -> Result<TokenStream>;
59
60 fn to_parts(
64 &self,
65 thecrate: &TokenStream,
66 package: Option<&LiteralString>,
67 address_map: &HashMap<Ident, TokenStream>,
68 ) -> Result<ModuleParts>;
69}
70
71#[sealed::sealed]
72impl ModuleGen for Module {
73 fn to_rust(
74 &self,
75 thecrate: &TokenStream,
76 package: Option<&LiteralString>,
77 address_map: &HashMap<Ident, TokenStream>,
78 ) -> Result<TokenStream> {
79 let ModuleParts { ident, docs, body } = self.to_parts(thecrate, package, address_map)?;
80 Ok(quote! {
81 #docs
82 #[allow(rustdoc::all, clippy::too_long_first_doc_paragraph)]
83 pub mod #ident {
84 #body
85 }
86 })
87 }
88
89 fn to_parts(
90 &self,
91 thecrate: &TokenStream,
92 package: Option<&LiteralString>,
93 address_map: &HashMap<Ident, TokenStream>,
94 ) -> Result<ModuleParts> {
95 let (docs, other) = crate::attributes::extract(&self.attrs)
96 .map_err(|err| format!("Parsing `moverox` attributes: {err}"))?;
97
98 if !other.is_empty() {
99 return Err("Move modules cannot have custom `moverox` attributes".into());
100 }
101
102 let ident = self.ident.clone();
103 let item_ctx = ItemContext {
104 thecrate,
105 package,
106 module: Some(&ident),
107 address_map,
108 };
109 let datatypes: TokenStream = self
110 .items()
111 .map(|item| item.to_rust(item_ctx))
112 .collect::<Result<_>>()?;
113
114 let body = quote! {
115 #[allow(non_camel_case_types, unused)]
116 type address = #thecrate::types::Address;
117 #[allow(non_camel_case_types, unused)]
118 type u256 = #thecrate::types::U256;
119 #[allow(non_camel_case_types, unused)]
120 type vector<T> = ::std::vec::Vec<T>;
121
122 #datatypes
123 };
124
125 Ok(ModuleParts { ident, docs, body })
126 }
127}
128
129#[derive(Clone, Copy)]
131pub struct ItemContext<'a> {
132 pub thecrate: &'a TokenStream,
138 pub package: Option<&'a LiteralString>,
140 pub module: Option<&'a Ident>,
142 pub address_map: &'a HashMap<Ident, TokenStream>,
146}
147
148#[sealed::sealed]
149pub trait ItemGen {
150 fn to_rust(&self, ctx: ItemContext<'_>) -> Result<TokenStream>;
151}
152
153#[sealed::sealed]
154impl ItemGen for Item {
155 fn to_rust(&self, ctx: ItemContext<'_>) -> Result<TokenStream> {
156 use move_syn::ItemKind as K;
157 let Self { attrs, kind, .. } = self;
158
159 let (docs, generated) = match kind {
160 K::Struct(s) => {
161 let err_ctx = |err| format!("struct {}: {err}", s.ident);
162 let (docs, otw_types) = crate::attributes::extract(attrs).map_err(err_ctx)?;
163 let generated = s.to_rust(otw_types, ctx).map_err(err_ctx)?;
164 (docs, generated)
165 }
166 K::Enum(e) => {
167 let err_ctx = |err| format!("enum {}: {err}", e.ident);
168 let (docs, otw_types) = crate::attributes::extract(attrs).map_err(err_ctx)?;
169 let generated = self::move_enum::to_rust(e, otw_types, ctx).map_err(err_ctx)?;
170 (docs, generated)
171 }
172 _ => return non_datatype_gen(attrs),
173 };
174
175 Ok(quote! {
176 #docs
177 #generated
178 })
179 }
180}
181
182fn non_datatype_gen(attrs: &[Attributes]) -> Result<TokenStream> {
183 if attrs.iter().flat_map(self::attributes::as_moverox).count() > 0 {
184 return Err(
185 "Move items other than enums/structs cannot be annotated with custom \
186 `moverox` attributes"
187 .into(),
188 );
189 }
190 Ok(TokenStream::new())
191}