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 let indexed = self.indexed;38 quote! {39 <SolidityEventArgument<#ty>>::new(#indexed, #camel_name)40 }41 }42}4344struct Event {45 name: Ident,46 name_screaming: Ident,47 fields: Vec<EventField>,48 selector: [u8; 32],49 selector_str: String,50}5152impl Event {53 fn try_from(variant: &Variant) -> syn::Result<Self> {54 let name = &variant.ident;55 let name_screaming = snake_ident_to_screaming(name);5657 let named = match &variant.fields {58 Fields::Named(named) => named,59 _ => {60 return Err(syn::Error::new(61 variant.fields.span(),62 "expected named fields",63 ))64 }65 };66 let mut fields = Vec::new();67 for field in &named.named {68 fields.push(EventField::try_from(field)?);69 }70 if fields.iter().filter(|f| f.indexed).count() > 3 {71 return Err(syn::Error::new(72 variant.fields.span(),73 "events can have at most 4 indexed fields (1 indexed field is reserved for event signature)"74 ));75 }76 let mut selector_str = format!("{}(", name);77 for (i, arg) in fields.iter().enumerate() {78 if i != 0 {79 write!(selector_str, ",").unwrap();80 }81 write!(selector_str, "{}", arg.ty).unwrap();82 }83 selector_str.push(')');84 let selector = crate::event_selector_str(&selector_str);8586 Ok(Self {87 name: name.to_owned(),88 name_screaming,89 fields,90 selector,91 selector_str,92 })93 }9495 fn expand_serializers(&self) -> proc_macro2::TokenStream {96 let name = &self.name;97 let name_screaming = &self.name_screaming;98 let fields = self.fields.iter().map(|f| &f.name);99100 let indexed = self.fields.iter().filter(|f| f.indexed).map(|f| &f.name);101 let plain = self.fields.iter().filter(|f| !f.indexed).map(|f| &f.name);102103 quote! {104 Self::#name {#(105 #fields,106 )*} => {107 topics.push(topic::from(Self::#name_screaming));108 #(109 topics.push(#indexed.to_topic());110 )*111 #(112 #plain.abi_write(&mut writer);113 )*114 }115 }116 }117118 fn expand_consts(&self) -> proc_macro2::TokenStream {119 let name_screaming = &self.name_screaming;120 let selector_str = &self.selector_str;121 let selector = &self.selector;122123 quote! {124 #[doc = #selector_str]125 const #name_screaming: [u8; 32] = [#(126 #selector,127 )*];128 }129 }130131 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {132 let name = self.name.to_string();133 let args = self.fields.iter().map(EventField::expand_solidity_argument);134 quote! {135 SolidityEvent {136 name: #name,137 args: (138 #(139 #args,140 )*141 ),142 }143 }144 }145}146147pub struct Events {148 name: Ident,149 events: Vec<Event>,150}151152impl Events {153 pub fn try_from(data: &DeriveInput) -> syn::Result<Self> {154 let name = &data.ident;155 let en = match &data.data {156 Data::Enum(en) => en,157 _ => return Err(syn::Error::new(data.span(), "expected enum")),158 };159 let mut events = Vec::new();160 for variant in &en.variants {161 events.push(Event::try_from(variant)?);162 }163 Ok(Self {164 name: name.to_owned(),165 events,166 })167 }168 pub fn expand(&self) -> proc_macro2::TokenStream {169 let name = &self.name;170171 let consts = self.events.iter().map(Event::expand_consts);172 let serializers = self.events.iter().map(Event::expand_serializers);173 let solidity_name = self.name.to_string();174 let solidity_functions = self.events.iter().map(Event::expand_solidity_function);175176 quote! {177 impl #name {178 #(179 #consts180 )*181182 pub fn generate_solidity_interface(out_set: &mut sp_std::collections::btree_set::BTreeSet<string>, is_impl: bool) {183 use evm_coder::solidity::*;184 use core::fmt::Write;185 let interface = SolidityInterface {186 name: #solidity_name,187 is: &[],188 functions: (#(189 #solidity_functions,190 )*),191 };192 let mut out = string::new();193 out.push_str("// Inline\n");194 let _ = interface.format(is_impl, &mut out);195 out_set.insert(out);196 }197 }198199 #[automatically_derived]200 impl ::evm_coder::events::ToLog for #name {201 fn to_log(&self, contract: address) -> ::ethereum::Log {202 use ::evm_coder::events::ToTopic;203 use ::evm_coder::abi::AbiWrite;204 let mut writer = ::evm_coder::abi::AbiWriter::new();205 let mut topics = Vec::new();206 match self {207 #(208 #serializers,209 )*210 }211 ::ethereum::Log {212 address: contract,213 topics,214 data: writer.finish(),215 }216 }217 }218 }219 }220}