1use inflector::cases;2use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};3use std::fmt::Write;4use quote::quote;56use crate::{parse_ident_from_path, parse_ident_from_type, snake_ident_to_screaming};78struct EventField {9 name: Ident,10 camel_name: String,11 ty: Ident,12 indexed: bool,13}1415impl EventField {16 fn try_from(field: &Field) -> syn::Result<Self> {17 let name = field.ident.as_ref().unwrap();18 let ty = parse_ident_from_type(&field.ty, false)?;19 let mut indexed = false;20 for attr in &field.attrs {21 if let Ok(ident) = parse_ident_from_path(&attr.path, false) {22 if ident == "indexed" {23 indexed = true;24 }25 }26 }27 Ok(Self {28 name: name.to_owned(),29 camel_name: cases::camelcase::to_camel_case(&name.to_string()),30 ty: ty.to_owned(),31 indexed,32 })33 }34 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {35 let camel_name = &self.camel_name;36 let ty = &self.ty;37 quote! {38 <NamedArgument<#ty>>::new(#camel_name)39 }40 }41}4243struct Event {44 name: Ident,45 name_screaming: Ident,46 fields: Vec<EventField>,47 selector: [u8; 32],48 selector_str: String,49}5051impl Event {52 fn try_from(variant: &Variant) -> syn::Result<Self> {53 let name = &variant.ident;54 let name_screaming = snake_ident_to_screaming(name);5556 let named = match &variant.fields {57 Fields::Named(named) => named,58 _ => {59 return Err(syn::Error::new(60 variant.fields.span(),61 "expected named fields",62 ))63 }64 };65 let mut fields = Vec::new();66 for field in &named.named {67 fields.push(EventField::try_from(field)?);68 }69 if fields.iter().filter(|f| f.indexed).count() > 3 {70 return Err(syn::Error::new(71 variant.fields.span(),72 "events can have at most 4 indexed fields (1 indexed field is reserved for event signature)"73 ));74 }75 let mut selector_str = format!("{}(", name);76 for (i, arg) in fields.iter().enumerate() {77 if i != 0 {78 write!(selector_str, ",").unwrap();79 }80 write!(selector_str, "{}", arg.ty).unwrap();81 }82 selector_str.push(')');83 let selector = crate::event_selector_str(&selector_str);8485 Ok(Self {86 name: name.to_owned(),87 name_screaming,88 fields,89 selector,90 selector_str,91 })92 }9394 fn expand_serializers(&self) -> proc_macro2::TokenStream {95 let name = &self.name;96 let name_screaming = &self.name_screaming;97 let fields = self.fields.iter().map(|f| &f.name);9899 let indexed = self.fields.iter().filter(|f| f.indexed).map(|f| &f.name);100 let plain = self.fields.iter().filter(|f| !f.indexed).map(|f| &f.name);101102 quote! {103 Self::#name {#(104 #fields,105 )*} => {106 topics.push(topic::from(Self::#name_screaming));107 #(108 topics.push(#indexed.to_topic());109 )*110 #(111 #plain.abi_write(&mut writer);112 )*113 }114 }115 }116117 fn expand_consts(&self) -> proc_macro2::TokenStream {118 let name_screaming = &self.name_screaming;119 let selector_str = &self.selector_str;120 let selector = &self.selector;121122 quote! {123 #[doc = #selector_str]124 const #name_screaming: [u8; 32] = [#(125 #selector,126 )*];127 }128 }129130 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {131 let name = self.name.to_string();132 let args = self.fields.iter().map(EventField::expand_solidity_argument);133 quote! {134 SolidityEvent {135 name: #name,136 args: (137 #(138 #args,139 )*140 ),141 }142 }143 }144}145146pub struct Events {147 name: Ident,148 events: Vec<Event>,149}150151impl Events {152 pub fn try_from(data: &DeriveInput) -> syn::Result<Self> {153 let name = &data.ident;154 let en = match &data.data {155 Data::Enum(en) => en,156 _ => return Err(syn::Error::new(data.span(), "expected enum")),157 };158 let mut events = Vec::new();159 for variant in &en.variants {160 events.push(Event::try_from(variant)?);161 }162 Ok(Self {163 name: name.to_owned(),164 events,165 })166 }167 pub fn expand(&self) -> proc_macro2::TokenStream {168 let name = &self.name;169170 let consts = self.events.iter().map(Event::expand_consts);171 let serializers = self.events.iter().map(Event::expand_serializers);172 let solidity_name = self.name.to_string();173 let solidity_functions = self.events.iter().map(Event::expand_solidity_function);174175 quote! {176 impl #name {177 #(178 #consts179 )*180181 pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet<string>, is_impl: bool) {182 use evm_coder::solidity::*;183 use core::fmt::Write;184 let interface = SolidityInterface {185 name: #solidity_name,186 is: &[],187 functions: (#(188 #solidity_functions,189 )*),190 };191 let mut out = string::new();192 out.push_str("// Inline\n");193 let _ = interface.format(is_impl, &mut out);194 out_set.insert(out);195 }196 }197198 #[automatically_derived]199 impl ::evm_coder::events::ToLog for #name {200 fn to_log(&self, contract: address) -> ::ethereum::Log {201 use ::evm_coder::events::ToTopic;202 use ::evm_coder::abi::AbiWrite;203 let mut writer = ::evm_coder::abi::AbiWriter::new();204 let mut topics = Vec::new();205 match self {206 #(207 #serializers,208 )*209 }210 ::ethereum::Log {211 address: contract,212 topics,213 data: writer.finish(),214 }215 }216 }217 }218 }219}