--- a/crates/evm-coder/procedural/src/abi_derive.rs +++ b/crates/evm-coder/procedural/src/abi_derive.rs @@ -2,41 +2,22 @@ pub(crate) fn impl_abi_macro(ast: &syn::DeriveInput) -> syn::Result { let name = &ast.ident; - let docs = ast - .attrs - .iter() - .filter_map(|attr| { - if let Some(ps) = attr.path.segments.first() { - if ps.ident == "doc" { - let meta = match attr.parse_meta() { - Ok(meta) => meta, - Err(e) => return Some(Err(e)), - }; - match meta { - syn::Meta::NameValue(mnv) => match &mnv.lit { - syn::Lit::Str(ls) => return Some(Ok(ls.value())), - _ => unreachable!(), - }, - _ => unreachable!(), - } - } - } - None - }) - .collect::>>()?; + let docs = extract_docs(&ast.attrs)?; - let (is_named_fields, field_names, field_types, params_count) = match &ast.data { + let (is_named_fields, field_names, field_types, field_docs, params_count) = match &ast.data { syn::Data::Struct(ds) => match ds.fields { syn::Fields::Named(ref fields) => Ok(( true, fields.named.iter().enumerate().map(map_field_to_name), fields.named.iter().map(map_field_to_type), + fields.named.iter().map(map_field_to_doc), fields.named.len(), )), syn::Fields::Unnamed(ref fields) => Ok(( false, fields.unnamed.iter().enumerate().map(map_field_to_name), fields.unnamed.iter().map(map_field_to_type), + fields.unnamed.iter().map(map_field_to_doc), fields.unnamed.len(), )), syn::Fields::Unit => Err(syn::Error::new(name.span(), "Unit structs not supported")), @@ -61,7 +42,7 @@ let solidity_type = impl_solidity_type(name, field_types.clone(), params_count); let solidity_type_name = impl_solidity_type_name(name, field_types.clone(), params_count); let solidity_struct_collect = - impl_solidity_struct_collect(name, field_names, field_types, &docs); + impl_solidity_struct_collect(name, field_names, field_types, field_docs, &docs)?; Ok(quote! { #can_be_plcaed_in_vec @@ -74,10 +55,28 @@ }) } -fn impl_can_be_placed_in_vec(ident: &syn::Ident) -> proc_macro2::TokenStream { - quote! { - impl ::evm_coder::sealed::CanBePlacedInVec for #ident {} - } +fn extract_docs(attrs: &Vec) -> syn::Result> { + attrs + .iter() + .filter_map(|attr| { + if let Some(ps) = attr.path.segments.first() { + if ps.ident == "doc" { + let meta = match attr.parse_meta() { + Ok(meta) => meta, + Err(e) => return Some(Err(e)), + }; + match meta { + syn::Meta::NameValue(mnv) => match &mnv.lit { + syn::Lit::Str(ls) => return Some(Ok(ls.value())), + _ => unreachable!(), + }, + _ => unreachable!(), + } + } + } + None + }) + .collect() } fn map_field_to_name(field: (usize, &syn::Field)) -> syn::Ident { @@ -95,6 +94,16 @@ &field.ty } +fn map_field_to_doc(field: &syn::Field) -> Result, syn::Error> { + extract_docs(&field.attrs) +} + +fn impl_can_be_placed_in_vec(ident: &syn::Ident) -> proc_macro2::TokenStream { + quote! { + impl ::evm_coder::sealed::CanBePlacedInVec for #ident {} + } +} + fn impl_abi_type<'a>( name: &syn::Ident, field_types: impl Iterator + Clone, @@ -283,17 +292,30 @@ name: &syn::Ident, field_names: impl Iterator + Clone, field_types: impl Iterator + Clone, + field_docs: impl Iterator>> + Clone, docs: &Vec, -) -> proc_macro2::TokenStream { +) -> syn::Result { let string_name = name.to_string(); let name_type = field_names .into_iter() - .zip(field_types.into_iter()) - .map(|(name, ty)| { + .zip(field_types) + .zip(field_docs) + .map(|((name, ty), doc)| { + let field_docs = match doc { + Ok(doc) => doc.into_iter().enumerate().map(|(i, doc)| { + let doc = doc.trim(); + let dev = if i == 0 { " @dev" } else { "" }; + quote! { + writeln!(str, "///{} {}", #dev, #doc).unwrap(); + } + }), + Err(e) => unreachable!("{:?}", e), + }; let name = format!("{}", name); quote!( + #(#field_docs)* write!(str, "\t{} ", <#ty as ::evm_coder::solidity::StructCollect>::name()).unwrap(); - write!(str, "{};", #name).unwrap(); + writeln!(str, "{};", #name).unwrap(); ) }); let docs = docs.iter().enumerate().map(|(i, doc)| { @@ -304,7 +326,7 @@ } }); - quote! { + Ok(quote! { #[cfg(feature = "stubgen")] impl ::evm_coder::solidity::StructCollect for #name { fn name() -> String { @@ -322,5 +344,5 @@ str } } - } + }) }