git.delta.rocks / unique-network / refs/commits / 9b94d7cf0b98

difftreelog

feat allow generics in new coder macros

Yaroslav Bolyukin2021-07-20parent: #5262156.patch.diff
in: master

3 files changed

modifiedcrates/evm-coder-macros/src/lib.rsdiffbeforeafterboth
--- a/crates/evm-coder-macros/src/lib.rs
+++ b/crates/evm-coder-macros/src/lib.rs
@@ -103,8 +103,8 @@
 	}
 }
 
-fn parse_ident_from_segment(segment: &PathSegment) -> syn::Result<&Ident> {
-	if segment.arguments != PathArguments::None {
+fn parse_ident_from_segment(segment: &PathSegment, allow_generics: bool) -> syn::Result<&Ident> {
+	if segment.arguments != PathArguments::None && !allow_generics {
 		return Err(syn::Error::new(
 			segment.arguments.span(),
 			"unexpected generic type",
@@ -113,14 +113,14 @@
 	Ok(&segment.ident)
 }
 
-fn parse_ident_from_path(path: &Path) -> syn::Result<&Ident> {
+fn parse_ident_from_path(path: &Path, allow_generics: bool) -> syn::Result<&Ident> {
 	let segment = parse_path_segment(path)?;
-	parse_ident_from_segment(segment)
+	parse_ident_from_segment(segment, allow_generics)
 }
 
-fn parse_ident_from_type(ty: &Type) -> syn::Result<&Ident> {
+fn parse_ident_from_type(ty: &Type, allow_generics: bool) -> syn::Result<&Ident> {
 	let path = parse_path(ty)?;
-	parse_ident_from_path(path)
+	parse_ident_from_path(path, allow_generics)
 }
 
 // Gets T out of Result<T>
modifiedcrates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth
--- a/crates/evm-coder-macros/src/solidity_interface.rs
+++ b/crates/evm-coder-macros/src/solidity_interface.rs
@@ -4,10 +4,7 @@
 use darling::FromMeta;
 use inflector::cases;
 use std::fmt::Write;
-use syn::{
-	FnArg, Ident, ImplItem, ImplItemMethod, ItemImpl, Meta, NestedMeta, PatType, Path, ReturnType,
-	Type, spanned::Spanned,
-};
+use syn::{FnArg, Generics, Ident, ImplItem, ImplItemMethod, ItemImpl, Meta, NestedMeta, PatType, Path, ReturnType, Type, spanned::Spanned};
 
 use crate::{
 	fn_selector_str, parse_ident_from_pat, parse_ident_from_path, parse_ident_from_type,
@@ -22,7 +19,7 @@
 }
 impl Is {
 	fn try_from(path: &Path) -> syn::Result<Self> {
-		let name = parse_ident_from_path(path)?.clone();
+		let name = parse_ident_from_path(path, false)?.clone();
 		Ok(Self {
 			pascal_call_name: pascal_ident_to_call(&name),
 			snake_call_name: pascal_ident_to_snake_call(&name),
@@ -115,7 +112,7 @@
 	fn try_from(value: &PatType) -> syn::Result<Self> {
 		Ok(Self {
 			name: parse_ident_from_pat(&value.pat)?.clone(),
-			ty: parse_ident_from_type(&value.ty)?.clone(),
+			ty: parse_ident_from_type(&value.ty, false)?.clone(),
 		})
 	}
 	fn is_value(&self) -> bool {
@@ -193,7 +190,7 @@
 			rename_selector: None,
 		};
 		for attr in &value.attrs {
-			let ident = parse_ident_from_path(&attr.path)?;
+			let ident = parse_ident_from_path(&attr.path, false)?;
 			if ident == "solidity" {
 				let args = attr.parse_meta().unwrap();
 				info = MethodInfo::from_meta(&args).unwrap();
@@ -382,8 +379,8 @@
 }
 
 pub struct SolidityInterface {
+    generics: Generics,
 	name: Box<syn::Type>,
-	ident: Ident,
 	info: InterfaceInfo,
 	methods: Vec<Method>,
 }
@@ -397,8 +394,8 @@
 			}
 		}
 		Ok(Self {
+            generics: value.generics.clone(),
 			name: value.self_ty.clone(),
-			ident: parse_ident_from_type(&value.self_ty)?.clone(),
 			info,
 			methods,
 		})
@@ -407,6 +404,7 @@
 		let name = self.name;
 
 		let call_name = pascal_ident_to_call(&self.info.name);
+        let generics = self.generics;
 
 		let call_sub = self
 			.info
@@ -485,7 +483,7 @@
 					return Ok(None);
 				}
 			}
-			impl ::evm_coder::Callable<#call_name> for #name {
+			impl #generics ::evm_coder::Callable<#call_name> for #name {
 				#[allow(unreachable_code)] // In case of no inner calls
 				fn call(&mut self, c: Msg<#call_name>) -> Result<::evm_coder::abi::AbiWriter> {
 					use ::evm_coder::abi::AbiWrite;
modifiedcrates/evm-coder-macros/src/to_log.rsdiffbeforeafterboth
before · crates/evm-coder-macros/src/to_log.rs
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)?;17		let mut indexed = false;18		for attr in &field.attrs {19			if let Ok(ident) = parse_ident_from_path(&attr.path) {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}
after · crates/evm-coder-macros/src/to_log.rs
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}