feat: add ethabitype support for solidity style enums (#526)

* feat: tokenize solidity like enums

* test: add enum test

* rustfmt
This commit is contained in:
Matthias Seitz 2021-10-24 19:58:41 +02:00 committed by GitHub
parent b072b05515
commit 5779a3cdaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 7 deletions

View File

@ -1,7 +1,7 @@
//! Helper functions for deriving `EthAbiType` //! Helper functions for deriving `EthAbiType`
use ethers_contract_abigen::ethers_core_crate; use ethers_contract_abigen::ethers_core_crate;
use proc_macro2::{Ident, TokenStream}; use proc_macro2::{Ident, Literal, TokenStream};
use quote::{quote, quote_spanned}; use quote::{quote, quote_spanned};
use syn::spanned::Spanned as _; use syn::spanned::Spanned as _;
use syn::{parse::Error, Data, DeriveInput, Fields, Variant}; use syn::{parse::Error, Data, DeriveInput, Fields, Variant};
@ -198,6 +198,12 @@ fn tokenize_unit_type(name: &Ident) -> TokenStream {
} }
} }
/// Derive for an enum
///
/// An enum can be a [solidity enum](https://docs.soliditylang.org/en/v0.5.3/types.html#enums) or a
/// bundled set of different types.
///
/// Decoding works like untagged decoding
fn tokenize_enum<'a>( fn tokenize_enum<'a>(
enum_name: &Ident, enum_name: &Ident,
variants: impl Iterator<Item = &'a Variant> + 'a, variants: impl Iterator<Item = &'a Variant> + 'a,
@ -206,15 +212,24 @@ fn tokenize_enum<'a>(
let mut into_tokens = TokenStream::new(); let mut into_tokens = TokenStream::new();
let mut from_tokens = TokenStream::new(); let mut from_tokens = TokenStream::new();
for variant in variants { for (idx, variant) in variants.into_iter().enumerate() {
let var_ident = &variant.ident;
if variant.fields.len() > 1 { if variant.fields.len() > 1 {
return Err(Error::new( return Err(Error::new(
variant.span(), variant.span(),
"EthAbiType cannot be derived for enum variants with multiple fields", "EthAbiType cannot be derived for enum variants with multiple fields",
)); ));
} } else if variant.fields.is_empty() {
let var_ident = &variant.ident; let value = Literal::u8_unsuffixed(idx as u8);
if let Some(field) = variant.fields.iter().next() { from_tokens.extend(quote! {
if let Ok(#value) = u8::from_token(token.clone()) {
return Ok(#enum_name::#var_ident)
}
});
into_tokens.extend(quote! {
#enum_name::#var_ident => #value.into_token(),
});
} else if let Some(field) = variant.fields.iter().next() {
let ty = &field.ty; let ty = &field.ty;
from_tokens.extend(quote! { from_tokens.extend(quote! {
if let Ok(decoded) = #ty::from_token(token.clone()) { if let Ok(decoded) = #ty::from_token(token.clone()) {
@ -226,8 +241,8 @@ fn tokenize_enum<'a>(
}); });
} else { } else {
into_tokens.extend(quote! { into_tokens.extend(quote! {
#enum_name::#var_ident(element) => # ethers_core::abi::Token::Tuple(::std::vec::Vec::new()), #enum_name::#var_ident(element) => # ethers_core::abi::Token::Tuple(::std::vec::Vec::new()),
}); });
} }
} }

View File

@ -489,3 +489,21 @@ fn can_derive_ethcall_with_nested_structs() {
assert_tokenizeable::<FooCall>(); assert_tokenizeable::<FooCall>();
assert_ethcall::<FooCall>(); assert_ethcall::<FooCall>();
} }
#[test]
fn can_derive_for_enum() {
#[derive(Debug, Clone, PartialEq, EthAbiType)]
enum ActionChoices {
GoLeft,
GoRight,
GoStraight,
SitStill,
}
assert_tokenizeable::<ActionChoices>();
let token = ActionChoices::GoLeft.into_token();
assert_eq!(
ActionChoices::GoLeft,
ActionChoices::from_token(token).unwrap()
);
}