1use quote::quote;23pub(crate) fn impl_abi_macro(ast: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {4 let name = &ast.ident;5 let docs = extract_docs(&ast.attrs)?;67 let (is_named_fields, field_names, field_types, field_docs, params_count) = match &ast.data {8 syn::Data::Struct(ds) => match ds.fields {9 syn::Fields::Named(ref fields) => Ok((10 true,11 fields.named.iter().enumerate().map(map_field_to_name),12 fields.named.iter().map(map_field_to_type),13 fields.named.iter().map(map_field_to_doc),14 fields.named.len(),15 )),16 syn::Fields::Unnamed(ref fields) => Ok((17 false,18 fields.unnamed.iter().enumerate().map(map_field_to_name),19 fields.unnamed.iter().map(map_field_to_type),20 fields.unnamed.iter().map(map_field_to_doc),21 fields.unnamed.len(),22 )),23 syn::Fields::Unit => Err(syn::Error::new(name.span(), "Unit structs not supported")),24 },25 syn::Data::Enum(_) => Err(syn::Error::new(name.span(), "Enums not supported")),26 syn::Data::Union(_) => Err(syn::Error::new(name.span(), "Unions not supported")),27 }?;2829 if params_count == 0 {30 return Err(syn::Error::new(name.span(), "Empty structs not supported"));31 };3233 let tuple_type = tuple_type(field_types.clone());34 let tuple_ref_type = tuple_ref_type(field_types.clone());35 let tuple_data = tuple_data_as_ref(is_named_fields, field_names.clone());36 let tuple_names = tuple_names(is_named_fields, field_names.clone());37 let struct_from_tuple = struct_from_tuple(name, is_named_fields, field_names.clone());3839 let can_be_plcaed_in_vec = impl_can_be_placed_in_vec(name);40 let abi_type = impl_abi_type(name, tuple_type.clone());41 let abi_read = impl_abi_read(name, tuple_type, tuple_names, struct_from_tuple);42 let abi_write = impl_abi_write(name, is_named_fields, tuple_ref_type, tuple_data);43 let solidity_type = impl_solidity_type(name, field_types.clone(), params_count);44 let solidity_type_name = impl_solidity_type_name(name, field_types.clone(), params_count);45 let solidity_struct_collect =46 impl_solidity_struct_collect(name, field_names, field_types, field_docs, &docs)?;4748 Ok(quote! {49 #can_be_plcaed_in_vec50 #abi_type51 #abi_read52 #abi_write53 #solidity_type54 #solidity_type_name55 #solidity_struct_collect56 })57}5859fn tuple_type<'a>(60 field_types: impl Iterator<Item = &'a syn::Type> + Clone,61) -> proc_macro2::TokenStream {62 let field_types = field_types.map(|ty| quote!(#ty,));63 quote! {(#(#field_types)*)}64}6566fn tuple_ref_type<'a>(67 field_types: impl Iterator<Item = &'a syn::Type> + Clone,68) -> proc_macro2::TokenStream {69 let field_types = field_types.map(|ty| quote!(&#ty,));70 quote! {(#(#field_types)*)}71}7273fn tuple_data_as_ref(74 is_named_fields: bool,75 field_names: impl Iterator<Item = syn::Ident> + Clone,76) -> proc_macro2::TokenStream {77 let field_names = field_names.enumerate().map(|(i, field)| {78 if is_named_fields {79 quote!(&self.#field,)80 } else {81 let field = proc_macro2::Literal::usize_unsuffixed(i);82 quote!(&self.#field,)83 }84 });85 quote! {(#(#field_names)*)}86}8788fn tuple_names(89 is_named_fields: bool,90 field_names: impl Iterator<Item = syn::Ident> + Clone,91) -> proc_macro2::TokenStream {92 let field_names = field_names.enumerate().map(|(i, field)| {93 if is_named_fields {94 quote!(#field,)95 } else {96 let field = proc_macro2::Ident::new(97 format!("field{}", i).as_str(),98 proc_macro2::Span::call_site(),99 );100 quote!(#field,)101 }102 });103 quote! {(#(#field_names)*)}104}105106fn struct_from_tuple(107 name: &syn::Ident,108 is_named_fields: bool,109 field_names: impl Iterator<Item = syn::Ident> + Clone,110) -> proc_macro2::TokenStream {111 let field_names = field_names.enumerate().map(|(i, field)| {112 if is_named_fields {113 quote!(#field,)114 } else {115 let field = proc_macro2::Ident::new(116 format!("field{}", i).as_str(),117 proc_macro2::Span::call_site(),118 );119 quote!(#field,)120 }121 });122123 if is_named_fields {124 quote! {#name {#(#field_names)*}}125 } else {126 quote! {#name (#(#field_names)*)}127 }128}129130fn extract_docs(attrs: &[syn::Attribute]) -> syn::Result<Vec<String>> {131 attrs132 .iter()133 .filter_map(|attr| {134 if let Some(ps) = attr.path.segments.first() {135 if ps.ident == "doc" {136 let meta = match attr.parse_meta() {137 Ok(meta) => meta,138 Err(e) => return Some(Err(e)),139 };140 match meta {141 syn::Meta::NameValue(mnv) => match &mnv.lit {142 syn::Lit::Str(ls) => return Some(Ok(ls.value())),143 _ => unreachable!(),144 },145 _ => unreachable!(),146 }147 }148 }149 None150 })151 .collect()152}153154fn map_field_to_name(field: (usize, &syn::Field)) -> syn::Ident {155 match field.1.ident.as_ref() {156 Some(name) => name.clone(),157 None => {158 let mut name = "field".to_string();159 name.push_str(field.0.to_string().as_str());160 syn::Ident::new(name.as_str(), proc_macro2::Span::call_site())161 }162 }163}164165fn map_field_to_type(field: &syn::Field) -> &syn::Type {166 &field.ty167}168169fn map_field_to_doc(field: &syn::Field) -> Result<Vec<std::string::String>, syn::Error> {170 extract_docs(&field.attrs)171}172173fn impl_can_be_placed_in_vec(ident: &syn::Ident) -> proc_macro2::TokenStream {174 quote! {175 impl ::evm_coder::sealed::CanBePlacedInVec for #ident {}176 }177}178179fn impl_abi_type(180 name: &syn::Ident,181 tuple_type: proc_macro2::TokenStream,182) -> proc_macro2::TokenStream {183 quote! {184 impl ::evm_coder::abi::AbiType for #name {185 const SIGNATURE: ::evm_coder::custom_signature::SignatureUnit = <#tuple_type as ::evm_coder::abi::AbiType>::SIGNATURE;186 fn is_dynamic() -> bool {187 <#tuple_type as ::evm_coder::abi::AbiType>::is_dynamic()188 }189 fn size() -> usize {190 <#tuple_type as ::evm_coder::abi::AbiType>::size()191 }192 }193 }194}195196fn impl_abi_read(197 name: &syn::Ident,198 tuple_type: proc_macro2::TokenStream,199 tuple_names: proc_macro2::TokenStream,200 struct_from_tuple: proc_macro2::TokenStream,201) -> proc_macro2::TokenStream {202 quote!(203 impl ::evm_coder::abi::AbiRead for #name {204 fn abi_read(reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Self> {205 let #tuple_names = <#tuple_type as ::evm_coder::abi::AbiRead>::abi_read(reader)?;206 Ok(#struct_from_tuple)207 }208 }209 )210}211212fn impl_abi_write(213 name: &syn::Ident,214 _is_named_fields: bool,215 tuple_type: proc_macro2::TokenStream,216 tuple_data: proc_macro2::TokenStream,217) -> proc_macro2::TokenStream {218 quote!(219 impl ::evm_coder::abi::AbiWrite for #name {220 fn abi_write(&self, writer: &mut ::evm_coder::abi::AbiWriter) {221 <#tuple_type as ::evm_coder::abi::AbiWrite>::abi_write(&#tuple_data, writer)222 }223 }224 )225}226227fn impl_solidity_type<'a>(228 name: &syn::Ident,229 field_types: impl Iterator<Item = &'a syn::Type> + Clone,230 params_count: usize,231) -> proc_macro2::TokenStream {232 let len = proc_macro2::Literal::usize_suffixed(params_count);233 quote! {234 #[cfg(feature = "stubgen")]235 impl ::evm_coder::solidity::SolidityType for #name {236 fn names(tc: &::evm_coder::solidity::TypeCollector) -> Vec<String> {237 let mut collected =238 Vec::with_capacity(<Self as ::evm_coder::solidity::SolidityType>::len());239 #({240 let mut out = String::new();241 <#field_types as ::evm_coder::solidity::SolidityTypeName>::solidity_name(&mut out, tc)242 .expect("no fmt error");243 collected.push(out);244 })*245 collected246 }247248 fn len() -> usize {249 #len250 }251 }252 }253}254255fn impl_solidity_type_name<'a>(256 name: &syn::Ident,257 field_types: impl Iterator<Item = &'a syn::Type> + Clone,258 params_count: usize,259) -> proc_macro2::TokenStream {260 let arg_dafaults = field_types.enumerate().map(|(i, ty)| {261 let mut defult_value = quote!(<#ty as ::evm_coder::solidity::SolidityTypeName262 >::solidity_default(writer, tc)?;);263 let last_item = params_count - 1;264 if i != last_item {265 defult_value.extend(quote! {write!(writer, ",")?;})266 }267 defult_value268 });269270 quote! {271 #[cfg(feature = "stubgen")]272 impl ::evm_coder::solidity::SolidityTypeName for #name {273 fn solidity_name(274 writer: &mut impl ::core::fmt::Write,275 tc: &::evm_coder::solidity::TypeCollector,276 ) -> ::core::fmt::Result {277 write!(writer, "{}", tc.collect_struct::<Self>())278 }279280 fn is_simple() -> bool {281 false282 }283284 fn solidity_default(285 writer: &mut impl ::core::fmt::Write,286 tc: &::evm_coder::solidity::TypeCollector,287 ) -> ::core::fmt::Result {288 write!(writer, "{}(", tc.collect_struct::<Self>())?;289290 #(#arg_dafaults)*291292 write!(writer, ")")293 }294 }295 }296}297298fn impl_solidity_struct_collect<'a>(299 name: &syn::Ident,300 field_names: impl Iterator<Item = proc_macro2::Ident> + Clone,301 field_types: impl Iterator<Item = &'a syn::Type> + Clone,302 field_docs: impl Iterator<Item = syn::Result<Vec<String>>> + Clone,303 docs: &[String],304) -> syn::Result<proc_macro2::TokenStream> {305 let string_name = name.to_string();306 let name_type = field_names307 .into_iter()308 .zip(field_types)309 .zip(field_docs)310 .map(|((name, ty), doc)| {311 let field_docs = match doc {312 Ok(doc) => doc.into_iter().enumerate().map(|(i, doc)| {313 let doc = doc.trim();314 let dev = if i == 0 { " @dev" } else { "" };315 quote! {316 writeln!(str, "\t///{} {}", #dev, #doc).unwrap();317 }318 }),319 Err(e) => unreachable!("{:?}", e),320 };321 let name = format!("{}", name);322 quote!(323 #(#field_docs)*324 write!(str, "\t{} ", <#ty as ::evm_coder::solidity::StructCollect>::name()).unwrap();325 writeln!(str, "{};", #name).unwrap();326 )327 });328 let docs = docs.iter().enumerate().map(|(i, doc)| {329 let doc = doc.trim();330 let dev = if i == 0 { " @dev" } else { "" };331 quote! {332 writeln!(str, "///{} {}", #dev, #doc).unwrap();333 }334 });335336 Ok(quote! {337 #[cfg(feature = "stubgen")]338 impl ::evm_coder::solidity::StructCollect for #name {339 fn name() -> String {340 #string_name.into()341 }342343 fn declaration() -> String {344 use std::fmt::Write;345346 let mut str = String::new();347 #(#docs)*348 writeln!(str, "struct {} {{", Self::name()).unwrap();349 #(#name_type)*350 writeln!(str, "}}").unwrap();351 str352 }353 }354 })355}