chore: update ethers-contract-derive to use syn 2
This commit is contained in:
parent
ef5a37b2e5
commit
85f6710471
|
@ -40,8 +40,7 @@ impl Contracts {
|
|||
|
||||
impl Parse for Contracts {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let inner =
|
||||
input.parse_terminated::<_, Token![;]>(ContractArgs::parse)?.into_iter().collect();
|
||||
let inner = input.parse_terminated(ContractArgs::parse, Token![;])?.into_iter().collect();
|
||||
Ok(Self { inner })
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +121,7 @@ impl Parse for Parameter {
|
|||
"methods" => {
|
||||
let content;
|
||||
braced!(content in input);
|
||||
let parsed = content.parse_terminated::<_, Token![;]>(Spanned::<Method>::parse)?;
|
||||
let parsed = content.parse_terminated(Spanned::<Method>::parse, Token![;])?;
|
||||
|
||||
let mut methods = Vec::with_capacity(parsed.len());
|
||||
let mut signatures = HashSet::new();
|
||||
|
@ -141,7 +140,7 @@ impl Parse for Parameter {
|
|||
"derives" | "event_derives" => {
|
||||
let content;
|
||||
parenthesized!(content in input);
|
||||
let derives = content.parse_terminated::<_, Token![,]>(Path::parse)?;
|
||||
let derives = content.parse_terminated(Path::parse, Token![,])?;
|
||||
Ok(Parameter::Derives(derives))
|
||||
}
|
||||
_ => Err(Error::new(name.span(), "unexpected named parameter")),
|
||||
|
@ -168,7 +167,7 @@ impl Parse for Method {
|
|||
// function params
|
||||
let content;
|
||||
parenthesized!(content in input);
|
||||
let params = content.parse_terminated::<_, Token![,]>(Ident::parse)?;
|
||||
let params = content.parse_terminated(Ident::parse, Token![,])?;
|
||||
let last_i = params.len().saturating_sub(1);
|
||||
|
||||
signature.push('(');
|
||||
|
|
|
@ -11,12 +11,10 @@ use syn::{parse::Error, DeriveInput};
|
|||
|
||||
/// Generates the `ethcall` trait support
|
||||
pub(crate) fn derive_eth_call_impl(input: DeriveInput) -> Result<TokenStream, Error> {
|
||||
let attributes = parse_calllike_attributes(&input, "ethcall")?;
|
||||
let attributes = parse_calllike_attributes!(input, "ethcall");
|
||||
|
||||
let function_call_name =
|
||||
attributes.name.map(|(s, _)| s).unwrap_or_else(|| input.ident.to_string());
|
||||
|
||||
let mut function = if let Some((abi, span)) = attributes.abi {
|
||||
let function_call_name = attributes.name(&input.ident);
|
||||
let mut function = if let Some((abi, span)) = attributes.abi() {
|
||||
let sig = abi.trim_start_matches("function ").trim_start();
|
||||
// try to parse as solidity function
|
||||
match HumanReadableParser::parse_function(&abi) {
|
||||
|
|
|
@ -7,97 +7,50 @@ use ethers_core::{
|
|||
};
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{parse::Error, spanned::Spanned, AttrStyle, DeriveInput, Lit, Meta, NestedMeta};
|
||||
use syn::{DeriveInput, LitStr, Result};
|
||||
|
||||
/// All the attributes the `EthCall`/`EthError` macro supports
|
||||
#[derive(Default)]
|
||||
pub struct EthCalllikeAttributes {
|
||||
pub name: Option<(String, Span)>,
|
||||
pub abi: Option<(String, Span)>,
|
||||
pub name: Option<LitStr>,
|
||||
pub abi: Option<LitStr>,
|
||||
}
|
||||
|
||||
/// extracts the attributes from the struct annotated with the given attribute
|
||||
pub fn parse_calllike_attributes(
|
||||
input: &DeriveInput,
|
||||
attr_name: &str,
|
||||
) -> Result<EthCalllikeAttributes, Error> {
|
||||
let mut result = EthCalllikeAttributes::default();
|
||||
for a in input.attrs.iter() {
|
||||
if let AttrStyle::Outer = a.style {
|
||||
if let Ok(Meta::List(meta)) = a.parse_meta() {
|
||||
if meta.path.is_ident(attr_name) {
|
||||
for n in meta.nested.iter() {
|
||||
if let NestedMeta::Meta(meta) = n {
|
||||
match meta {
|
||||
Meta::Path(path) => {
|
||||
return Err(Error::new(
|
||||
path.span(),
|
||||
format!("unrecognized {attr_name} parameter"),
|
||||
))
|
||||
}
|
||||
Meta::List(meta) => {
|
||||
return Err(Error::new(
|
||||
meta.path.span(),
|
||||
format!("unrecognized {attr_name} parameter"),
|
||||
))
|
||||
}
|
||||
Meta::NameValue(meta) => {
|
||||
if meta.path.is_ident("name") {
|
||||
if let Lit::Str(ref lit_str) = meta.lit {
|
||||
if result.name.is_none() {
|
||||
result.name =
|
||||
Some((lit_str.value(), lit_str.span()));
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"name already specified",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"name must be a string",
|
||||
))
|
||||
}
|
||||
} else if meta.path.is_ident("abi") {
|
||||
if let Lit::Str(ref lit_str) = meta.lit {
|
||||
if result.abi.is_none() {
|
||||
result.abi =
|
||||
Some((lit_str.value(), lit_str.span()));
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"abi already specified",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"abi must be a string",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
format!("unrecognized {attr_name} parameter"),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl EthCalllikeAttributes {
|
||||
pub fn name(&self, fallback: &Ident) -> String {
|
||||
self.name.as_ref().map(|s| s.value()).unwrap_or_else(|| fallback.to_string())
|
||||
}
|
||||
|
||||
pub fn abi(&self) -> Option<(String, Span)> {
|
||||
self.abi.as_ref().map(|s| (s.value(), s.span()))
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
macro_rules! parse_calllike_attributes {
|
||||
($input:ident, $attr_ident:literal) => {{
|
||||
let mut result = EthCalllikeAttributes::default();
|
||||
$crate::utils::parse_attributes!($input.attrs.iter(), $attr_ident, meta,
|
||||
"name", result.name => {
|
||||
meta.input.parse::<::syn::Token![=]>()?;
|
||||
let litstr: ::syn::LitStr = meta.input.parse()?;
|
||||
result.name = Some(litstr);
|
||||
}
|
||||
"abi", result.abi => {
|
||||
meta.input.parse::<::syn::Token![=]>()?;
|
||||
let litstr: ::syn::LitStr = meta.input.parse()?;
|
||||
result.abi = Some(litstr);
|
||||
}
|
||||
);
|
||||
result
|
||||
}};
|
||||
}
|
||||
pub(crate) use parse_calllike_attributes;
|
||||
|
||||
/// Generates the decode implementation based on the type's runtime `AbiType` impl
|
||||
pub fn derive_decode_impl_with_abi_type(
|
||||
input: &DeriveInput,
|
||||
trait_ident: Ident,
|
||||
) -> Result<TokenStream, Error> {
|
||||
) -> Result<TokenStream> {
|
||||
let datatypes_array = utils::abi_parameters_array(input, &trait_ident.to_string())?;
|
||||
Ok(derive_decode_impl(datatypes_array, trait_ident))
|
||||
}
|
||||
|
@ -130,7 +83,7 @@ pub fn derive_codec_impls(
|
|||
input: &DeriveInput,
|
||||
decode_impl: TokenStream,
|
||||
trait_ident: Ident,
|
||||
) -> Result<TokenStream, Error> {
|
||||
) -> Result<TokenStream> {
|
||||
// the ethers crates to use
|
||||
let ethers_core = ethers_core_crate();
|
||||
let ethers_contract = ethers_contract_crate();
|
||||
|
|
|
@ -11,11 +11,10 @@ use syn::{parse::Error, DeriveInput};
|
|||
|
||||
/// Generates the `EthError` trait support
|
||||
pub(crate) fn derive_eth_error_impl(input: DeriveInput) -> Result<TokenStream, Error> {
|
||||
let attributes = parse_calllike_attributes(&input, "etherror")?;
|
||||
let attributes = parse_calllike_attributes!(input, "etherror");
|
||||
|
||||
let error_name = attributes.name.map(|(s, _)| s).unwrap_or_else(|| input.ident.to_string());
|
||||
|
||||
let mut error = if let Some((src, span)) = attributes.abi {
|
||||
let error_name = attributes.name(&input.ident);
|
||||
let mut error = if let Some((src, span)) = attributes.abi() {
|
||||
let raw_function_sig = src.trim_start_matches("error ").trim_start();
|
||||
// try to parse as solidity error
|
||||
match HumanReadableParser::parse_error(&src) {
|
||||
|
|
|
@ -6,16 +6,12 @@ use ethers_core::{
|
|||
abi::{Event, EventExt, EventParam, HumanReadableParser},
|
||||
macros::{ethers_contract_crate, ethers_core_crate},
|
||||
};
|
||||
use hex::FromHex;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse::Error, spanned::Spanned, AttrStyle, Data, DeriveInput, Field, Fields, Lit, Meta,
|
||||
NestedMeta,
|
||||
};
|
||||
use syn::{spanned::Spanned, Data, DeriveInput, Error, Field, Fields, LitStr, Result, Token};
|
||||
|
||||
/// Generates the `EthEvent` trait support
|
||||
pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> Result<TokenStream, Error> {
|
||||
pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> Result<TokenStream> {
|
||||
let name = &input.ident;
|
||||
let attributes = parse_event_attributes(&input)?;
|
||||
|
||||
|
@ -64,7 +60,7 @@ pub(crate) fn derive_eth_event_impl(input: DeriveInput) -> Result<TokenStream, E
|
|||
|
||||
let (abi, event_sig) = (event.abi_signature(), event.signature());
|
||||
|
||||
let signature = if let Some((hash, _)) = attributes.signature_hash {
|
||||
let signature = if let Some((hash, _)) = attributes.signature {
|
||||
utils::signature(&hash)
|
||||
} else {
|
||||
utils::signature(event_sig.as_bytes())
|
||||
|
@ -122,7 +118,7 @@ impl EventField {
|
|||
}
|
||||
}
|
||||
|
||||
fn derive_decode_from_log_impl(input: &DeriveInput, event: &Event) -> Result<TokenStream, Error> {
|
||||
fn derive_decode_from_log_impl(input: &DeriveInput, event: &Event) -> Result<TokenStream> {
|
||||
let ethers_core = ethers_core_crate();
|
||||
|
||||
let fields: Vec<_> = match input.data {
|
||||
|
@ -291,7 +287,7 @@ fn derive_decode_from_log_impl(input: &DeriveInput, event: &Event) -> Result<Tok
|
|||
}
|
||||
|
||||
/// Determine the event's ABI by parsing the AST
|
||||
fn derive_abi_event_from_fields(input: &DeriveInput) -> Result<Event, Error> {
|
||||
fn derive_abi_event_from_fields(input: &DeriveInput) -> Result<Event> {
|
||||
let event = Event {
|
||||
name: input.ident.to_string(),
|
||||
inputs: utils::derive_abi_inputs_from_fields(input, "EthEvent")?
|
||||
|
@ -303,53 +299,18 @@ fn derive_abi_event_from_fields(input: &DeriveInput) -> Result<Event, Error> {
|
|||
Ok(event)
|
||||
}
|
||||
|
||||
fn parse_field_attributes(field: &Field) -> Result<(Option<String>, bool), Error> {
|
||||
let mut indexed = false;
|
||||
let mut topic_name = None;
|
||||
for a in field.attrs.iter() {
|
||||
if let AttrStyle::Outer = a.style {
|
||||
if let Ok(Meta::List(meta)) = a.parse_meta() {
|
||||
if meta.path.is_ident("ethevent") {
|
||||
for n in meta.nested.iter() {
|
||||
if let NestedMeta::Meta(meta) = n {
|
||||
match meta {
|
||||
Meta::Path(path) => {
|
||||
if path.is_ident("indexed") {
|
||||
indexed = true;
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
path.span(),
|
||||
"unrecognized ethevent parameter",
|
||||
))
|
||||
}
|
||||
}
|
||||
Meta::List(meta) => {
|
||||
return Err(Error::new(
|
||||
meta.path.span(),
|
||||
"unrecognized ethevent parameter",
|
||||
))
|
||||
}
|
||||
Meta::NameValue(meta) => {
|
||||
if meta.path.is_ident("name") {
|
||||
if let Lit::Str(ref lit_str) = meta.lit {
|
||||
topic_name = Some(lit_str.value());
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"name attribute must be a string",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn parse_field_attributes(field: &Field) -> Result<(Option<String>, bool)> {
|
||||
let mut indexed = None::<bool>;
|
||||
let mut topic_name = None::<String>;
|
||||
utils::parse_attributes!(field.attrs.iter(), "ethevent", meta,
|
||||
"indexed", indexed => { indexed = Some(true) }
|
||||
"name", topic_name => {
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let litstr: LitStr = meta.input.parse()?;
|
||||
topic_name = Some(litstr.value());
|
||||
}
|
||||
}
|
||||
|
||||
Ok((topic_name, indexed))
|
||||
);
|
||||
Ok((topic_name, indexed.unwrap_or_default()))
|
||||
}
|
||||
|
||||
/// All the attributes the `EthEvent` macro supports
|
||||
|
@ -357,139 +318,32 @@ fn parse_field_attributes(field: &Field) -> Result<(Option<String>, bool), Error
|
|||
struct EthEventAttributes {
|
||||
name: Option<(String, Span)>,
|
||||
abi: Option<(String, Span)>,
|
||||
signature_hash: Option<(Vec<u8>, Span)>,
|
||||
signature: Option<(Vec<u8>, Span)>,
|
||||
anonymous: Option<(bool, Span)>,
|
||||
}
|
||||
|
||||
/// extracts the attributes from the struct annotated with `EthEvent`
|
||||
fn parse_event_attributes(input: &DeriveInput) -> Result<EthEventAttributes, Error> {
|
||||
fn parse_event_attributes(input: &DeriveInput) -> Result<EthEventAttributes> {
|
||||
let mut result = EthEventAttributes::default();
|
||||
for a in input.attrs.iter() {
|
||||
if let AttrStyle::Outer = a.style {
|
||||
if let Ok(Meta::List(meta)) = a.parse_meta() {
|
||||
if meta.path.is_ident("ethevent") {
|
||||
for n in meta.nested.iter() {
|
||||
if let NestedMeta::Meta(meta) = n {
|
||||
match meta {
|
||||
Meta::Path(path) => {
|
||||
if let Some(name) = path.get_ident() {
|
||||
if &*name.to_string() == "anonymous" {
|
||||
if result.anonymous.is_none() {
|
||||
result.anonymous = Some((true, name.span()));
|
||||
continue
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
name.span(),
|
||||
"anonymous already specified",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
return Err(Error::new(
|
||||
path.span(),
|
||||
"unrecognized ethevent parameter",
|
||||
))
|
||||
}
|
||||
Meta::List(meta) => {
|
||||
return Err(Error::new(
|
||||
meta.path.span(),
|
||||
"unrecognized ethevent parameter",
|
||||
))
|
||||
}
|
||||
Meta::NameValue(meta) => {
|
||||
if meta.path.is_ident("anonymous") {
|
||||
if let Lit::Bool(ref bool_lit) = meta.lit {
|
||||
if result.anonymous.is_none() {
|
||||
result.anonymous =
|
||||
Some((bool_lit.value, bool_lit.span()));
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"anonymous already specified",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"name must be a string",
|
||||
))
|
||||
}
|
||||
} else if meta.path.is_ident("name") {
|
||||
if let Lit::Str(ref lit_str) = meta.lit {
|
||||
if result.name.is_none() {
|
||||
result.name =
|
||||
Some((lit_str.value(), lit_str.span()));
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"name already specified",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"name must be a string",
|
||||
))
|
||||
}
|
||||
} else if meta.path.is_ident("abi") {
|
||||
if let Lit::Str(ref lit_str) = meta.lit {
|
||||
if result.abi.is_none() {
|
||||
result.abi =
|
||||
Some((lit_str.value(), lit_str.span()));
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"abi already specified",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"abi must be a string",
|
||||
))
|
||||
}
|
||||
} else if meta.path.is_ident("signature") {
|
||||
if let Lit::Str(ref lit_str) = meta.lit {
|
||||
if result.signature_hash.is_none() {
|
||||
match Vec::from_hex(lit_str.value()) {
|
||||
Ok(sig) => {
|
||||
result.signature_hash =
|
||||
Some((sig, lit_str.span()))
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
format!(
|
||||
"Expected hex signature: {err:?}"
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"signature already specified",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"signature must be a hex string",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
meta.span(),
|
||||
"unrecognized ethevent parameter",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
utils::parse_attributes!(input.attrs.iter(), "ethevent", meta,
|
||||
"name", result.name => {
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let litstr: LitStr = meta.input.parse()?;
|
||||
result.name = Some((litstr.value(), litstr.span()));
|
||||
}
|
||||
}
|
||||
"abi", result.abi => {
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let litstr: LitStr = meta.input.parse()?;
|
||||
result.abi = Some((litstr.value(), litstr.span()));
|
||||
}
|
||||
"signature", result.signature => {
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
let litstr: LitStr = meta.input.parse()?;
|
||||
let s = litstr.value();
|
||||
let b = hex::decode(s.strip_prefix("0x").unwrap_or(&s)).map_err(|e| meta.error(e))?;
|
||||
result.signature = Some((b, litstr.span()));
|
||||
}
|
||||
"anonymous", result.anonymous => { result.anonymous = Some((true, meta.path.span())); }
|
||||
);
|
||||
Ok(result)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,35 @@ use syn::{
|
|||
PathArguments, Type,
|
||||
};
|
||||
|
||||
/// Parses the specified attributes from a `syn::Attribute` iterator.
|
||||
macro_rules! parse_attributes {
|
||||
($attrs:expr, $attr_ident:literal, $meta:ident, $($field:pat, $opt:expr => $block:block)*) => {
|
||||
const ERROR: &str = concat!("unrecognized ", $attr_ident, " attribute");
|
||||
const ALREADY_SPECIFIED: &str = concat!($attr_ident, " attribute already specified");
|
||||
|
||||
for attr in $attrs {
|
||||
if !attr.path().is_ident($attr_ident) {
|
||||
continue;
|
||||
}
|
||||
|
||||
attr.parse_nested_meta(|$meta| {
|
||||
let ident = $meta.path.get_ident().ok_or_else(|| $meta.error(ERROR))?.to_string();
|
||||
match ident.as_str() {
|
||||
$(
|
||||
$field if $opt.is_none() => $block,
|
||||
$field => return Err($meta.error(ALREADY_SPECIFIED)),
|
||||
)*
|
||||
|
||||
_ => return Err($meta.error(ERROR)),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(crate) use parse_attributes;
|
||||
|
||||
pub fn ident(name: &str) -> Ident {
|
||||
Ident::new(name, Span::call_site())
|
||||
}
|
||||
|
@ -104,6 +133,8 @@ pub fn param_type_quote(kind: &ParamType) -> TokenStream {
|
|||
/// Tries to find the corresponding `ParamType` used for tokenization for the
|
||||
/// given type
|
||||
pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> {
|
||||
const ERROR: &str = "Failed to derive proper ABI from array field";
|
||||
|
||||
match ty {
|
||||
Type::Array(arr) => {
|
||||
let ty = find_parameter_type(&arr.elem)?;
|
||||
|
@ -114,7 +145,7 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Err(Error::new(arr.span(), "Failed to derive proper ABI from array field"))
|
||||
Err(Error::new(arr.span(), ERROR))
|
||||
}
|
||||
|
||||
Type::Path(ty) => {
|
||||
|
@ -151,7 +182,7 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> {
|
|||
s => parse_param_type(s),
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| Error::new(ty.span(), "Failed to derive proper ABI from fields"))
|
||||
.ok_or_else(|| Error::new(ty.span(), ERROR))
|
||||
}
|
||||
|
||||
Type::Tuple(ty) => ty
|
||||
|
@ -161,7 +192,7 @@ pub fn find_parameter_type(ty: &Type) -> Result<ParamType, Error> {
|
|||
.collect::<Result<Vec<_>, _>>()
|
||||
.map(ParamType::Tuple),
|
||||
|
||||
_ => Err(Error::new(ty.span(), "Failed to derive proper ABI from fields")),
|
||||
_ => Err(Error::new(ty.span(), ERROR)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue