1use proc_macro::TokenStream;
25use proc_macro2::Span;
26use quote::quote;
27use syn::{LitStr, parse_macro_input, spanned::Spanned};
28
29mod abi;
30mod codegen;
31mod parser;
32
33use abi::MoveModuleABI;
34use codegen::generate_contract_impl;
35
36#[proc_macro]
78pub fn aptos_contract(input: TokenStream) -> TokenStream {
79 let input = parse_macro_input!(input as parser::ContractInput);
80
81 let abi: MoveModuleABI = match serde_json::from_str(&input.abi) {
83 Ok(abi) => abi,
84 Err(e) => {
85 return syn::Error::new(input.name.span(), format!("Failed to parse ABI JSON: {e}"))
86 .to_compile_error()
87 .into();
88 }
89 };
90
91 let source_info = input.source.as_ref().map(|s| parser::parse_move_source(s));
93
94 let tokens = generate_contract_impl(&input.name, &abi, source_info.as_ref());
96
97 tokens.into()
98}
99
100#[proc_macro]
110pub fn aptos_contract_file(input: TokenStream) -> TokenStream {
111 let input = parse_macro_input!(input as parser::FileInput);
112
113 let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string());
115 let file_path = std::path::Path::new(&manifest_dir).join(&input.path);
116
117 let abi_content = match std::fs::read_to_string(&file_path) {
118 Ok(content) => content,
119 Err(e) => {
120 return syn::Error::new(
122 input.name.span(),
123 format!("Failed to read ABI file '{}': {e}", file_path.display()),
124 )
125 .to_compile_error()
126 .into();
127 }
128 };
129
130 let abi: MoveModuleABI = match serde_json::from_str(&abi_content) {
131 Ok(abi) => abi,
132 Err(e) => {
133 return syn::Error::new(
134 input.name.span(),
135 format!(
136 "Failed to parse ABI JSON from '{}': {e}",
137 file_path.display(),
138 ),
139 )
140 .to_compile_error()
141 .into();
142 }
143 };
144
145 let source_info = if let Some(source_path) = input.source_path.as_ref() {
147 let source_file = std::path::Path::new(&manifest_dir).join(source_path);
148 match std::fs::read_to_string(&source_file) {
149 Ok(content) => Some(parser::parse_move_source(&content)),
150 Err(e) => {
151 return syn::Error::new(
152 input.name.span(),
153 format!(
154 "Failed to read Move source file '{}': {e}",
155 source_file.display(),
156 ),
157 )
158 .to_compile_error()
159 .into();
160 }
161 }
162 } else {
163 None
164 };
165
166 let tokens = generate_contract_impl(&input.name, &abi, source_info.as_ref());
167
168 tokens.into()
169}
170
171#[proc_macro_derive(MoveStruct, attributes(move_struct))]
189pub fn derive_move_struct(input: TokenStream) -> TokenStream {
190 let input = parse_macro_input!(input as syn::DeriveInput);
191
192 let name = &input.ident;
193
194 let mut address = None;
196 let mut module = None;
197 let mut struct_name = None;
198 let mut parse_error: Option<syn::Error> = None;
199
200 for attr in &input.attrs {
201 if attr.path().is_ident("move_struct") {
202 let result = attr.parse_nested_meta(|meta| {
203 if meta.path.is_ident("address") {
204 let value: LitStr = meta.value()?.parse()?;
205 address = Some(value.value());
206 } else if meta.path.is_ident("module") {
207 let value: LitStr = meta.value()?.parse()?;
208 module = Some(value.value());
209 } else if meta.path.is_ident("name") {
210 let value: LitStr = meta.value()?.parse()?;
211 struct_name = Some(value.value());
212 } else {
213 return Err(syn::Error::new(
214 meta.path.span(),
215 format!(
216 "Unknown attribute '{}'. Expected 'address', 'module', or 'name'",
217 meta.path
218 .get_ident()
219 .map_or_else(|| "?".to_string(), ToString::to_string)
220 ),
221 ));
222 }
223 Ok(())
224 });
225
226 if let Err(e) = result {
227 parse_error = Some(e);
228 break;
229 }
230 }
231 }
232
233 if let Some(e) = parse_error {
235 return e.to_compile_error().into();
236 }
237
238 let address = address.unwrap_or_else(|| "0x1".to_string());
239 let module = module.unwrap_or_else(|| "unknown".to_string());
240 let struct_name = struct_name.unwrap_or_else(|| name.to_string());
241
242 let type_tag = format!("{address}::{module}::{struct_name}");
243 let type_tag_lit = LitStr::new(&type_tag, Span::call_site());
245
246 let expanded = quote! {
247 impl #name {
248 pub fn type_tag() -> &'static str {
250 #type_tag_lit
251 }
252
253 pub fn to_bcs(&self) -> ::aptos_sdk::error::AptosResult<Vec<u8>> {
255 ::aptos_sdk::aptos_bcs::to_bytes(self)
256 .map_err(|e| ::aptos_sdk::error::AptosError::Bcs(e.to_string()))
257 }
258
259 pub fn from_bcs(bytes: &[u8]) -> ::aptos_sdk::error::AptosResult<Self> {
261 ::aptos_sdk::aptos_bcs::from_bytes(bytes)
262 .map_err(|e| ::aptos_sdk::error::AptosError::Bcs(e.to_string()))
263 }
264 }
265 };
266
267 expanded.into()
268}