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

difftreelog

feat generate solidity and abi traits for enums

Trubnikov Sergey2022-11-29parent: #a1ef1e7.patch.diff
in: master

3 files changed

modifiedcrates/evm-coder/procedural/src/abi_derive.rsdiffbeforeafterboth
--- a/crates/evm-coder/procedural/src/abi_derive.rs
+++ b/crates/evm-coder/procedural/src/abi_derive.rs
@@ -44,13 +44,14 @@
 	let struct_from_tuple = struct_from_tuple(name, is_named_fields, field_names.clone());
 
 	let can_be_plcaed_in_vec = impl_can_be_placed_in_vec(name);
-	let abi_type = impl_abi_type(name, tuple_type.clone());
-	let abi_read = impl_abi_read(name, tuple_type, tuple_names, struct_from_tuple);
-	let abi_write = impl_abi_write(name, is_named_fields, tuple_ref_type, tuple_data);
-	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 abi_type = impl_struct_abi_type(name, tuple_type.clone());
+	let abi_read = impl_struct_abi_read(name, tuple_type, tuple_names, struct_from_tuple);
+	let abi_write = impl_struct_abi_write(name, is_named_fields, tuple_ref_type, tuple_data);
+	let solidity_type = impl_struct_solidity_type(name, field_types.clone(), params_count);
+	let solidity_type_name =
+		impl_struct_solidity_type_name(name, field_types.clone(), params_count);
 	let solidity_struct_collect =
-		impl_solidity_struct_collect(name, field_names, field_types, field_docs, &docs)?;
+		impl_struct_solidity_struct_collect(name, field_names, field_types, field_docs, &docs)?;
 
 	Ok(quote! {
 		#can_be_plcaed_in_vec
@@ -69,14 +70,177 @@
 ) -> syn::Result<proc_macro2::TokenStream> {
 	let name = &ast.ident;
 	check_repr_u8(name, &ast.attrs)?;
-	check_option_validity(de)?;
+	let option_count = check_and_count_option(de)?;
+	let enum_options = de.variants.iter().map(|v| &v.ident);
+
+	let from = impl_enum_from_u8(name, enum_options.clone());
+	let solidity_option = impl_solidity_option(name, enum_options.clone());
+	let can_be_plcaed_in_vec = impl_can_be_placed_in_vec(name);
+	let abi_type = impl_enum_abi_type(name);
+	let abi_read = impl_enum_abi_read(name);
+	let abi_write = impl_enum_abi_write(name);
+	let solidity_type_name = impl_enum_solidity_type_name(name, enum_options.clone());
+	let solidity_struct_collect =
+		impl_enum_solidity_struct_collect(name, enum_options, option_count);
+
+	Ok(quote! {
+		#from
+		#solidity_option
+		#can_be_plcaed_in_vec
+		#abi_type
+		#abi_read
+		#abi_write
+		#solidity_type_name
+		#solidity_struct_collect
+	})
+}
+
+fn impl_solidity_option<'a>(
+	name: &proc_macro2::Ident,
+	enum_options: impl Iterator<Item = &'a syn::Ident>,
+) -> proc_macro2::TokenStream {
+	let enum_options = enum_options.map(|opt| {
+		let s = name.to_string() + "." + opt.to_string().as_str();
+		let as_string = proc_macro2::Literal::string(s.as_str());
+		quote!(#name::#opt => #as_string,)
+	});
+	quote!(
+		impl ::evm_coder::solidity::SolidityEnum for #name {
+			fn solidity_option(&self) -> &str {
+				match <#name>::default() {
+					#(#enum_options)*
+				}
+			}
+		}
+	)
+}
+
+fn impl_enum_from_u8<'a>(
+	name: &proc_macro2::Ident,
+	enum_options: impl Iterator<Item = &'a syn::Ident>,
+) -> proc_macro2::TokenStream {
+	let enum_options = enum_options.enumerate().map(|(i, opt)| {
+		let n = proc_macro2::Literal::u8_suffixed(i as u8);
+		quote! {#n => Ok(#name::#opt),}
+	});
+	quote!(
+		impl TryFrom<u8> for #name {
+			type Error = &'static str;
+
+			fn try_from(value: u8) -> ::std::result::Result<Self, Self::Error> {
+				const err: &'static str = "Not convertible";
+				match value {
+					#(#enum_options)*
+					_ => Err(err)
+				}
+			}
+		}
+	)
+}
 
-	dbg!(&de);
+fn impl_enum_abi_type(name: &syn::Ident) -> proc_macro2::TokenStream {
+	quote! {
+		impl ::evm_coder::abi::AbiType for #name {
+			const SIGNATURE: ::evm_coder::custom_signature::SignatureUnit = <u8 as ::evm_coder::abi::AbiType>::SIGNATURE;
 
-	Ok(quote!())
+			fn is_dynamic() -> bool {
+				<u8 as ::evm_coder::abi::AbiType>::is_dynamic()
+			}
+			fn size() -> usize {
+				<u8 as ::evm_coder::abi::AbiType>::size()
+			}
+		}
+	}
 }
 
-fn check_option_validity(de: &syn::DataEnum) -> syn::Result<()> {
+fn impl_enum_abi_read(name: &syn::Ident) -> proc_macro2::TokenStream {
+	quote!(
+		impl ::evm_coder::abi::AbiRead for #name {
+			fn abi_read(reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Self> {
+				Ok(
+					<u8 as ::evm_coder::abi::AbiRead>::abi_read(reader)?
+						.try_into()?
+				)
+			}
+		}
+	)
+}
+
+fn impl_enum_abi_write(name: &syn::Ident) -> proc_macro2::TokenStream {
+	quote!(
+		impl ::evm_coder::abi::AbiWrite for #name {
+			fn abi_write(&self, writer: &mut ::evm_coder::abi::AbiWriter) {
+				::evm_coder::abi::AbiWrite::abi_write(&(*self as u8), writer);
+			}
+		}
+	)
+}
+
+fn impl_enum_solidity_type_name<'a>(
+	name: &syn::Ident,
+	enum_options: impl Iterator<Item = &'a syn::Ident>,
+) -> proc_macro2::TokenStream {
+	let enum_options = enum_options.map(|opt| quote!(,));
+	quote!(
+		#[cfg(feature = "stubgen")]
+		impl ::evm_coder::solidity::SolidityTypeName for #name {
+			fn solidity_name(
+				writer: &mut impl ::core::fmt::Write,
+				tc: &::evm_coder::solidity::TypeCollector,
+			) -> ::core::fmt::Result {
+				write!(writer, "{}", tc.collect_struct::<Self>())
+			}
+
+			fn is_simple() -> bool {
+				true
+			}
+
+			fn solidity_default(
+				writer: &mut impl ::core::fmt::Write,
+				tc: &::evm_coder::solidity::TypeCollector,
+			) -> ::core::fmt::Result {
+				write!(writer, "{}", <#name as ::evm_coder::solidity::SolidityEnum>::solidity_option(&<#name>::default()))
+			}
+		}
+	)
+}
+
+fn impl_enum_solidity_struct_collect<'a>(
+	name: &syn::Ident,
+	enum_options: impl Iterator<Item = &'a syn::Ident>,
+	option_count: usize,
+) -> proc_macro2::TokenStream {
+	let string_name = name.to_string();
+	let enum_options = enum_options.enumerate().map(|(i, opt)| {
+		let opt = proc_macro2::Literal::string(opt.to_string().as_str());
+		let comma = if i != option_count - 1 { "," } else { "" };
+		quote! {
+			writeln!(str, "\t{}{}", #opt, #comma).expect("Enum format option");
+		}
+	});
+	quote!(
+		#[cfg(feature = "stubgen")]
+		impl ::evm_coder::solidity::StructCollect for #name {
+			fn name() -> String {
+				#string_name.into()
+			}
+
+			fn declaration() -> String {
+				use std::fmt::Write;
+
+				let mut str = String::new();
+				// #(#docs)*
+				writeln!(str, "enum {} {{", <Self as ::evm_coder::solidity::StructCollect>::name()).unwrap();
+				#(#enum_options)*
+				writeln!(str, "}}").unwrap();
+				str
+			}
+		}
+	)
+}
+
+fn check_and_count_option(de: &syn::DataEnum) -> syn::Result<usize> {
+	let mut count = 0;
 	for error in de.variants.iter().filter_map(|v| {
 		if !v.fields.is_empty() {
 			Some(Err(syn::Error::new(
@@ -89,13 +253,14 @@
 				"Enumeration options should not have an explicit specified value",
 			)))
 		} else {
+			count += 1;
 			None
 		}
 	}) {
 		return error;
 	}
 
-	Ok(())
+	Ok(count)
 }
 
 fn check_repr_u8(name: &syn::Ident, attrs: &Vec<syn::Attribute>) -> syn::Result<()> {
@@ -271,7 +436,7 @@
 	}
 }
 
-fn impl_abi_type(
+fn impl_struct_abi_type(
 	name: &syn::Ident,
 	tuple_type: proc_macro2::TokenStream,
 ) -> proc_macro2::TokenStream {
@@ -288,7 +453,7 @@
 	}
 }
 
-fn impl_abi_read(
+fn impl_struct_abi_read(
 	name: &syn::Ident,
 	tuple_type: proc_macro2::TokenStream,
 	tuple_names: proc_macro2::TokenStream,
@@ -304,7 +469,7 @@
 	)
 }
 
-fn impl_abi_write(
+fn impl_struct_abi_write(
 	name: &syn::Ident,
 	_is_named_fields: bool,
 	tuple_type: proc_macro2::TokenStream,
@@ -319,7 +484,7 @@
 	)
 }
 
-fn impl_solidity_type<'a>(
+fn impl_struct_solidity_type<'a>(
 	name: &syn::Ident,
 	field_types: impl Iterator<Item = &'a syn::Type> + Clone,
 	params_count: usize,
@@ -347,7 +512,7 @@
 	}
 }
 
-fn impl_solidity_type_name<'a>(
+fn impl_struct_solidity_type_name<'a>(
 	name: &syn::Ident,
 	field_types: impl Iterator<Item = &'a syn::Type> + Clone,
 	params_count: usize,
@@ -390,7 +555,7 @@
 	}
 }
 
-fn impl_solidity_struct_collect<'a>(
+fn impl_struct_solidity_struct_collect<'a>(
 	name: &syn::Ident,
 	field_names: impl Iterator<Item = proc_macro2::Ident> + Clone,
 	field_types: impl Iterator<Item = &'a syn::Type> + Clone,
modifiedcrates/evm-coder/src/solidity/traits.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/solidity/traits.rs
+++ b/crates/evm-coder/src/solidity/traits.rs
@@ -8,6 +8,10 @@
 	fn declaration() -> String;
 }
 
+pub trait SolidityEnum: 'static {
+	fn solidity_option(&self) -> &str;
+}
+
 pub trait SolidityTypeName: 'static {
 	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
 	/// "simple" types are stored inline, no `memory` modifier should be used in solidity
modifiedcrates/evm-coder/tests/abi_derive_generation.rsdiffbeforeafterboth
7575
76 #[test]76 #[test]
77 #[cfg(feature = "stubgen")]77 #[cfg(feature = "stubgen")]
78 fn struct_collect_TypeStruct3DerivedMixedParam() {78 fn struct_collect_type_struct3_derived_mixed_param() {
79 assert_eq!(79 assert_eq!(
80 <TypeStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::name(),80 <TypeStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::name(),
81 "TypeStruct3DerivedMixedParam"81 "TypeStruct3DerivedMixedParam"
296296
297 #[test]297 #[test]
298 #[cfg(feature = "stubgen")]298 #[cfg(feature = "stubgen")]
299 fn struct_collect_TupleStruct3DerivedMixedParam() {299 fn struct_collect_tuple_struct3_derived_mixed_param() {
300 assert_eq!(300 assert_eq!(
301 <TupleStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::name(),301 <TupleStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::name(),
302 "TupleStruct3DerivedMixedParam"302 "TupleStruct3DerivedMixedParam"
739mod test_enum {739mod test_enum {
740 use evm_coder::AbiCoder;740 use evm_coder::AbiCoder;
741741
742 #[derive(AbiCoder)]742 #[derive(AbiCoder, Default)]
743 #[repr(u8)]743 #[repr(u8)]
744 enum Color {744 enum Color {
745 Red,745 Red,
746 Green,746 Green,
747 #[default]
747 Blue,748 Blue,
748 }749 }
749750