git.delta.rocks / unique-network / refs/commits / a68e61d3d55f

difftreelog

source

crates/evm-coder-macros/src/to_log.rs3.9 KiBsourcehistory
1use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};2use std::fmt::Write;3use quote::quote;45use crate::{parse_ident_from_path, parse_ident_from_type, snake_ident_to_screaming};67struct EventField {8	name: Ident,9	ty: Ident,10	indexed: bool,11}1213impl EventField {14	fn try_from(field: &Field) -> syn::Result<Self> {15		let name = field.ident.as_ref().unwrap();16		let ty = parse_ident_from_type(&field.ty, false)?;17		let mut indexed = false;18		for attr in &field.attrs {19			if let Ok(ident) = parse_ident_from_path(&attr.path, false) {20				if ident == "indexed" {21					indexed = true;22				}23			}24		}25		Ok(Self {26			name: name.to_owned(),27			ty: ty.to_owned(),28			indexed,29		})30	}31}3233struct Event {34	name: Ident,35	name_screaming: Ident,36	fields: Vec<EventField>,37	selector: [u8; 32],38	selector_str: String,39}4041impl Event {42	fn try_from(variant: &Variant) -> syn::Result<Self> {43		let name = &variant.ident;44		let name_screaming = snake_ident_to_screaming(name);4546		let named = match &variant.fields {47			Fields::Named(named) => named,48			_ => {49				return Err(syn::Error::new(50					variant.fields.span(),51					"expected named fields",52				))53			}54		};55		let mut fields = Vec::new();56		for field in &named.named {57			fields.push(EventField::try_from(field)?);58		}59		if fields.iter().filter(|f| f.indexed).count() > 3 {60			return Err(syn::Error::new(61				variant.fields.span(),62				"events can have at most 4 indexed fields (1 indexed field is reserved for event signature)"63			));64		}65		let mut selector_str = format!("{}(", name);66		for (i, arg) in fields.iter().enumerate() {67			if i != 0 {68				write!(selector_str, ",").unwrap();69			}70			write!(selector_str, "{}", arg.ty).unwrap();71		}72		selector_str.push(')');73		let selector = crate::event_selector_str(&selector_str);7475		Ok(Self {76			name: name.to_owned(),77			name_screaming,78			fields,79			selector,80			selector_str,81		})82	}8384	fn expand_serializers(&self) -> proc_macro2::TokenStream {85		let name = &self.name;86		let name_screaming = &self.name_screaming;87		let fields = self.fields.iter().map(|f| &f.name);8889		let indexed = self.fields.iter().filter(|f| f.indexed).map(|f| &f.name);90		let plain = self.fields.iter().filter(|f| !f.indexed).map(|f| &f.name);9192		quote! {93			Self::#name {#(94				#fields,95			)*} => {96				topics.push(topic::from(Self::#name_screaming));97				#(98					topics.push(#indexed.to_topic());99				)*100				#(101					#plain.abi_write(&mut writer);102				)*103			}104		}105	}106107	fn expand_consts(&self) -> proc_macro2::TokenStream {108		let name_screaming = &self.name_screaming;109		let selector_str = &self.selector_str;110		let selector = &self.selector;111112		quote! {113			#[doc = #selector_str]114			const #name_screaming: [u8; 32] = [#(115				#selector,116			)*];117		}118	}119}120121pub struct Events {122	name: Ident,123	events: Vec<Event>,124}125126impl Events {127	pub fn try_from(data: &DeriveInput) -> syn::Result<Self> {128		let name = &data.ident;129		let en = match &data.data {130			Data::Enum(en) => en,131			_ => return Err(syn::Error::new(data.span(), "expected enum")),132		};133		let mut events = Vec::new();134		for variant in &en.variants {135			events.push(Event::try_from(variant)?);136		}137		Ok(Self {138			name: name.to_owned(),139			events,140		})141	}142	pub fn expand(&self) -> proc_macro2::TokenStream {143		let name = &self.name;144145		let consts = self.events.iter().map(Event::expand_consts);146		let serializers = self.events.iter().map(Event::expand_serializers);147148		quote! {149			impl #name {150				#(151					#consts152				)*153			}154155			#[automatically_derived]156			impl ::evm_coder::events::ToLog for #name {157				fn to_log(&self, contract: address) -> ::ethereum::Log {158					use ::evm_coder::events::ToTopic;159					use ::evm_coder::abi::AbiWrite;160					let mut writer = ::evm_coder::abi::AbiWriter::new();161					let mut topics = Vec::new();162					match self {163						#(164							#serializers,165						)*166					}167					::ethereum::Log {168						address: contract,169						topics,170						data: writer.finish(),171					}172				}173			}174		}175	}176}