refactor(abigen): solidity types expansion (#2131)
* refactor: event input expansion * refactor: common expand params * refactor: method params expansion * refactor: struct fields expansion * refactor: types array expansion * clippy
This commit is contained in:
parent
0c16eb971d
commit
f9ced12fee
|
@ -1,66 +1,8 @@
|
|||
use super::{types, util, Context};
|
||||
use ethers_core::{
|
||||
abi::{Param, ParamType},
|
||||
macros::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate},
|
||||
};
|
||||
use super::Context;
|
||||
use ethers_core::macros::{ethers_contract_crate, ethers_core_crate, ethers_providers_crate};
|
||||
use proc_macro2::{Ident, Literal, TokenStream};
|
||||
use quote::quote;
|
||||
|
||||
/// Expands to the `name : type` pairs for the params
|
||||
pub(crate) fn expand_params<'a, F>(
|
||||
params: &[Param],
|
||||
resolve_tuple: F,
|
||||
) -> eyre::Result<Vec<(TokenStream, TokenStream)>>
|
||||
where
|
||||
F: Fn(&str) -> Option<&'a str>,
|
||||
{
|
||||
params
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, param)| {
|
||||
let name = util::expand_input_name(idx, ¶m.name);
|
||||
let ty = expand_param_type(param, ¶m.kind, |s| resolve_tuple(s))?;
|
||||
Ok((name, ty))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// returns the Tokenstream for the corresponding rust type
|
||||
pub(crate) fn expand_param_type<'a, F>(
|
||||
param: &Param,
|
||||
kind: &ParamType,
|
||||
resolve_tuple: F,
|
||||
) -> eyre::Result<TokenStream>
|
||||
where
|
||||
F: Fn(&str) -> Option<&'a str>,
|
||||
{
|
||||
match kind {
|
||||
ParamType::Array(ty) => {
|
||||
let ty = expand_param_type(param, ty, resolve_tuple)?;
|
||||
Ok(quote! {
|
||||
::std::vec::Vec<#ty>
|
||||
})
|
||||
}
|
||||
ParamType::FixedArray(ty, size) => {
|
||||
let ty = expand_param_type(param, ty, resolve_tuple)?;
|
||||
let size = *size;
|
||||
Ok(quote! {[#ty; #size]})
|
||||
}
|
||||
ParamType::Tuple(_) => {
|
||||
let ty = if let Some(rust_struct_name) =
|
||||
param.internal_type.as_ref().and_then(|s| resolve_tuple(s.as_str()))
|
||||
{
|
||||
let ident = util::ident(rust_struct_name);
|
||||
quote! {#ident}
|
||||
} else {
|
||||
types::expand(kind)?
|
||||
};
|
||||
Ok(ty)
|
||||
}
|
||||
_ => types::expand(kind),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn imports(name: &str) -> TokenStream {
|
||||
let doc_str = format!("{name} was auto-generated with ethers-rs Abigen. More information at: <https://github.com/gakonst/ethers-rs>");
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
//! derive error bindings
|
||||
|
||||
use super::{util, Context};
|
||||
use crate::contract::common::{expand_data_struct, expand_data_tuple, expand_params};
|
||||
use super::{
|
||||
common::{expand_data_struct, expand_data_tuple},
|
||||
types, util, Context,
|
||||
};
|
||||
use ethers_core::{
|
||||
abi::{ethabi::AbiError, ErrorExt},
|
||||
macros::{ethers_contract_crate, ethers_core_crate},
|
||||
|
@ -81,7 +83,9 @@ impl Context {
|
|||
|
||||
/// Expands to the `name : type` pairs of the function's outputs
|
||||
fn expand_error_params(&self, error: &AbiError) -> Result<Vec<(TokenStream, TokenStream)>> {
|
||||
expand_params(&error.inputs, |s| self.internal_structs.get_struct_type(s))
|
||||
types::expand_params(&error.inputs, |p| {
|
||||
p.internal_type.as_deref().and_then(|s| self.internal_structs.get_struct_type(s))
|
||||
})
|
||||
}
|
||||
|
||||
/// The name ident of the errors enum
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use super::{types, util, Context};
|
||||
use crate::util::can_derive_defaults;
|
||||
use ethers_core::{
|
||||
abi::{Event, EventExt, EventParam, Param, ParamType},
|
||||
abi::{Event, EventExt, Param},
|
||||
macros::{ethers_contract_crate, ethers_core_crate},
|
||||
};
|
||||
use eyre::Result;
|
||||
use inflector::Inflector;
|
||||
use proc_macro2::{Ident, Literal, TokenStream};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::quote;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
|
@ -136,85 +136,6 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
/// Expands an event property type.
|
||||
///
|
||||
/// Note that this is slightly different from expanding a Solidity type as
|
||||
/// complex types like arrays and strings get emitted as hashes when they are
|
||||
/// indexed.
|
||||
/// If a complex types matches with a struct previously parsed by the internal structs,
|
||||
/// we can replace it
|
||||
fn expand_input_type(
|
||||
&self,
|
||||
event: &Event,
|
||||
input: &EventParam,
|
||||
idx: usize,
|
||||
) -> Result<TokenStream> {
|
||||
let ethers_core = ethers_core_crate();
|
||||
Ok(match (&input.kind, input.indexed) {
|
||||
(ParamType::Array(_), true) => {
|
||||
quote! { #ethers_core::types::H256 }
|
||||
}
|
||||
(ParamType::FixedArray(_, _), true) => {
|
||||
quote! { #ethers_core::types::H256 }
|
||||
}
|
||||
(ParamType::Tuple(..), true) => {
|
||||
quote! { #ethers_core::types::H256 }
|
||||
}
|
||||
(ParamType::Bytes, true) | (ParamType::String, true) => {
|
||||
quote! { #ethers_core::types::H256 }
|
||||
}
|
||||
(ParamType::Tuple(_), false) => {
|
||||
let ty = if let Some(rust_struct_name) =
|
||||
self.internal_structs.get_event_input_struct_type(&event.name, idx)
|
||||
{
|
||||
let ident = util::ident(rust_struct_name);
|
||||
quote! {#ident}
|
||||
} else {
|
||||
types::expand(&input.kind)?
|
||||
};
|
||||
ty
|
||||
}
|
||||
(ParamType::Array(_), _) => {
|
||||
// represents an array of a struct
|
||||
if let Some(rust_struct_name) =
|
||||
self.internal_structs.get_event_input_struct_type(&event.name, idx)
|
||||
{
|
||||
let ty = util::ident(rust_struct_name);
|
||||
return Ok(quote! {::std::vec::Vec<#ty>})
|
||||
}
|
||||
types::expand(&input.kind)?
|
||||
}
|
||||
(ParamType::FixedArray(_, size), _) => {
|
||||
// represents a fixed array of a struct
|
||||
if let Some(rust_struct_name) =
|
||||
self.internal_structs.get_event_input_struct_type(&event.name, idx)
|
||||
{
|
||||
let ty = util::ident(rust_struct_name);
|
||||
let size = Literal::usize_unsuffixed(*size);
|
||||
return Ok(quote! {[#ty; #size]})
|
||||
}
|
||||
types::expand(&input.kind)?
|
||||
}
|
||||
(kind, _) => types::expand(kind)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Expands the name-type pairs for the given inputs
|
||||
fn expand_event_params(&self, event: &Event) -> Result<Vec<(TokenStream, TokenStream, bool)>> {
|
||||
event
|
||||
.inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, input)| {
|
||||
// NOTE: Events can contain nameless values.
|
||||
let name = util::expand_input_name(idx, &input.name);
|
||||
let ty = self.expand_input_type(event, input, idx)?;
|
||||
|
||||
Ok((name, ty, input.indexed))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Expands into a single method for contracting an event stream.
|
||||
fn expand_filter(&self, event: &Event) -> TokenStream {
|
||||
let name = &event.name;
|
||||
|
@ -251,7 +172,7 @@ impl Context {
|
|||
|
||||
let event_name = event_struct_name(&event.name, sig);
|
||||
|
||||
let params = self.expand_event_params(event)?;
|
||||
let params = types::expand_event_inputs(event, &self.internal_structs)?;
|
||||
// expand as a tuple if all fields are anonymous
|
||||
let all_anonymous_fields = event.inputs.iter().all(|input| input.name.is_empty());
|
||||
let data_type_definition = if all_anonymous_fields {
|
||||
|
@ -355,6 +276,7 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::Abigen;
|
||||
use ethers_core::abi::{EventParam, Hash, ParamType};
|
||||
use proc_macro2::Literal;
|
||||
|
||||
/// Expands a 256-bit `Hash` into a literal representation that can be used with
|
||||
/// quasi-quoting for code generation. We do this to avoid allocating at runtime
|
||||
|
@ -445,7 +367,7 @@ mod tests {
|
|||
};
|
||||
|
||||
let cx = test_context();
|
||||
let params = cx.expand_event_params(&event).unwrap();
|
||||
let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
|
||||
let name = event_struct_name(&event.name, None);
|
||||
let definition = expand_data_struct(&name, ¶ms);
|
||||
|
||||
|
@ -469,7 +391,7 @@ mod tests {
|
|||
};
|
||||
|
||||
let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
|
||||
let params = cx.expand_event_params(&event).unwrap();
|
||||
let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
|
||||
let alias = Some(util::ident("FooAliased"));
|
||||
let name = event_struct_name(&event.name, alias);
|
||||
let definition = expand_data_struct(&name, ¶ms);
|
||||
|
@ -494,7 +416,7 @@ mod tests {
|
|||
};
|
||||
|
||||
let cx = test_context();
|
||||
let params = cx.expand_event_params(&event).unwrap();
|
||||
let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
|
||||
let name = event_struct_name(&event.name, None);
|
||||
let definition = expand_data_tuple(&name, ¶ms);
|
||||
|
||||
|
@ -515,7 +437,7 @@ mod tests {
|
|||
};
|
||||
|
||||
let cx = test_context_with_alias("Foo(bool,address)", "FooAliased");
|
||||
let params = cx.expand_event_params(&event).unwrap();
|
||||
let params = types::expand_event_inputs(&event, &cx.internal_structs).unwrap();
|
||||
let alias = Some(util::ident("FooAliased"));
|
||||
let name = event_struct_name(&event.name, alias);
|
||||
let definition = expand_data_tuple(&name, ¶ms);
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use std::collections::{btree_map::Entry, BTreeMap, HashMap, HashSet};
|
||||
|
||||
use super::{types, util, Context};
|
||||
use crate::{
|
||||
contract::common::{expand_data_struct, expand_data_tuple, expand_param_type, expand_params},
|
||||
util::can_derive_defaults,
|
||||
use super::{
|
||||
common::{expand_data_struct, expand_data_tuple},
|
||||
types, Context,
|
||||
};
|
||||
use crate::util::{self, can_derive_defaults};
|
||||
use ethers_core::{
|
||||
abi::{Function, FunctionExt, Param, ParamType},
|
||||
macros::{ethers_contract_crate, ethers_core_crate},
|
||||
|
@ -14,6 +12,7 @@ use eyre::{Context as _, Result};
|
|||
use inflector::Inflector;
|
||||
use proc_macro2::{Literal, TokenStream};
|
||||
use quote::quote;
|
||||
use std::collections::{btree_map::Entry, BTreeMap, HashMap, HashSet};
|
||||
use syn::Ident;
|
||||
|
||||
/// The maximum amount of overloaded functions that are attempted to auto aliased with their param
|
||||
|
@ -304,127 +303,45 @@ impl Context {
|
|||
|
||||
/// Expands to the `name : type` pairs of the function's inputs
|
||||
fn expand_input_params(&self, fun: &Function) -> Result<Vec<(TokenStream, TokenStream)>> {
|
||||
fun.inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, param)| {
|
||||
let name = util::expand_input_name(idx, ¶m.name);
|
||||
let ty = self.expand_input_param_type(fun, ¶m.name, ¶m.kind)?;
|
||||
Ok((name, ty))
|
||||
types::expand_params(&fun.inputs, |p| {
|
||||
self.internal_structs.get_function_input_struct_type(&fun.name, &p.name)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Expands to the `name : type` pairs of the function's outputs
|
||||
/// Expands to the `name: type` pairs of the function's outputs
|
||||
fn expand_output_params(&self, fun: &Function) -> Result<Vec<(TokenStream, TokenStream)>> {
|
||||
expand_params(&fun.outputs, |s| {
|
||||
self.internal_structs.get_function_output_struct_type(&fun.name, s)
|
||||
types::expand_params(&fun.outputs, |p| {
|
||||
p.internal_type
|
||||
.as_deref()
|
||||
.and_then(|s| self.internal_structs.get_function_output_struct_type(&fun.name, s))
|
||||
})
|
||||
}
|
||||
|
||||
/// Expands to the return type of a function
|
||||
fn expand_outputs(&self, fun: &Function) -> Result<TokenStream> {
|
||||
let mut outputs = Vec::with_capacity(fun.outputs.len());
|
||||
for param in fun.outputs.iter() {
|
||||
let ty = self.expand_output_param_type(fun, param, ¶m.kind)?;
|
||||
outputs.push(ty);
|
||||
}
|
||||
|
||||
let return_ty = match outputs.len() {
|
||||
0 => quote! { () },
|
||||
1 => outputs[0].clone(),
|
||||
_ => {
|
||||
quote! { (#( #outputs ),*) }
|
||||
}
|
||||
};
|
||||
Ok(return_ty)
|
||||
}
|
||||
|
||||
/// Expands the arguments for the call that eventually calls the contract
|
||||
fn expand_contract_call_args(&self, fun: &Function) -> Result<TokenStream> {
|
||||
let mut call_args = Vec::with_capacity(fun.inputs.len());
|
||||
for (idx, param) in fun.inputs.iter().enumerate() {
|
||||
fn expand_contract_call_args(&self, fun: &Function) -> TokenStream {
|
||||
let mut call_args = fun.inputs.iter().enumerate().map(|(idx, param)| {
|
||||
let name = util::expand_input_name(idx, ¶m.name);
|
||||
let call_arg = match param.kind {
|
||||
match param.kind {
|
||||
// this is awkward edge case where the function inputs are a single struct
|
||||
// we need to force this argument into a tuple so it gets expanded to `((#name,))`
|
||||
// this is currently necessary because internally `flatten_tokens` is called which
|
||||
// removes the outermost `tuple` level and since `((#name))` is not
|
||||
// a rust tuple it doesn't get wrapped into another tuple that will be peeled off by
|
||||
// `flatten_tokens`
|
||||
// we need to force this argument into a tuple so it gets expanded to
|
||||
// `((#name,))` this is currently necessary because
|
||||
// internally `flatten_tokens` is called which removes the
|
||||
// outermost `tuple` level and since `((#name))` is not
|
||||
// a rust tuple it doesn't get wrapped into another tuple that will be peeled
|
||||
// off by `flatten_tokens`
|
||||
ParamType::Tuple(_) if fun.inputs.len() == 1 => {
|
||||
// make sure the tuple gets converted to `Token::Tuple`
|
||||
quote! {(#name,)}
|
||||
quote!((#name,))
|
||||
}
|
||||
_ => name,
|
||||
};
|
||||
call_args.push(call_arg);
|
||||
}
|
||||
let call_args = match call_args.len() {
|
||||
0 => quote! { () },
|
||||
1 => quote! { #( #call_args )* },
|
||||
_ => quote! { ( #(#call_args, )* ) },
|
||||
};
|
||||
});
|
||||
|
||||
Ok(call_args)
|
||||
match fun.inputs.len() {
|
||||
0 => quote!(()),
|
||||
1 => call_args.next().unwrap(),
|
||||
_ => quote!(( #( #call_args ),* )),
|
||||
}
|
||||
|
||||
/// returns the Tokenstream for the corresponding rust type of the param
|
||||
fn expand_input_param_type(
|
||||
&self,
|
||||
fun: &Function,
|
||||
param: &str,
|
||||
kind: &ParamType,
|
||||
) -> Result<TokenStream> {
|
||||
let ethers_core = ethers_core_crate();
|
||||
match kind {
|
||||
ParamType::Array(ty) => {
|
||||
let ty = self.expand_input_param_type(fun, param, ty)?;
|
||||
Ok(quote! {
|
||||
::std::vec::Vec<#ty>
|
||||
})
|
||||
}
|
||||
ParamType::FixedArray(ty, size) => {
|
||||
let ty = match **ty {
|
||||
ParamType::Uint(size) => {
|
||||
if size / 8 == 1 {
|
||||
// this prevents type ambiguity with `FixedBytes`
|
||||
quote! { #ethers_core::types::Uint8}
|
||||
} else {
|
||||
self.expand_input_param_type(fun, param, ty)?
|
||||
}
|
||||
}
|
||||
_ => self.expand_input_param_type(fun, param, ty)?,
|
||||
};
|
||||
|
||||
let size = *size;
|
||||
Ok(quote! {[#ty; #size]})
|
||||
}
|
||||
ParamType::Tuple(_) => {
|
||||
let ty = if let Some(rust_struct_name) =
|
||||
self.internal_structs.get_function_input_struct_type(&fun.name, param)
|
||||
{
|
||||
let ident = util::ident(rust_struct_name);
|
||||
quote! {#ident}
|
||||
} else {
|
||||
types::expand(kind)?
|
||||
};
|
||||
Ok(ty)
|
||||
}
|
||||
_ => types::expand(kind),
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the TokenStream for the corresponding rust type of the output param
|
||||
fn expand_output_param_type(
|
||||
&self,
|
||||
fun: &Function,
|
||||
param: &Param,
|
||||
kind: &ParamType,
|
||||
) -> Result<TokenStream> {
|
||||
expand_param_type(param, kind, |s| {
|
||||
self.internal_structs.get_function_output_struct_type(&fun.name, s)
|
||||
})
|
||||
}
|
||||
|
||||
/// Expands a single function with the given alias
|
||||
|
@ -439,12 +356,22 @@ impl Context {
|
|||
|
||||
let selector_tokens = expand_selector(selector);
|
||||
|
||||
let contract_args = self.expand_contract_call_args(function)?;
|
||||
let contract_args = self.expand_contract_call_args(function);
|
||||
let function_params =
|
||||
self.expand_input_params(function)?.into_iter().map(|(name, ty)| quote! { #name: #ty });
|
||||
let function_params = quote! { #( , #function_params )* };
|
||||
|
||||
let outputs = self.expand_outputs(function)?;
|
||||
let outputs = {
|
||||
let mut out = self.expand_output_params(function)?;
|
||||
match out.len() {
|
||||
0 => quote!(()),
|
||||
1 => out.pop().unwrap().1,
|
||||
_ => {
|
||||
let iter = out.into_iter().map(|(_, ty)| ty);
|
||||
quote!(( #( #iter ),* ))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let doc_str =
|
||||
format!("Calls the contract's `{name}` (0x{}) function", hex::encode(selector));
|
||||
|
|
|
@ -93,7 +93,7 @@ impl Context {
|
|||
for field in sol_struct.fields() {
|
||||
let ty = match field.r#type() {
|
||||
FieldType::Elementary(ty) => types::expand(ty)?,
|
||||
FieldType::Struct(struct_ty) => expand_struct_type(struct_ty),
|
||||
FieldType::Struct(struct_ty) => types::expand_struct_type(struct_ty),
|
||||
FieldType::Mapping(_) => {
|
||||
eyre::bail!("Mapping types in struct `{}` are not supported {:?}", name, field)
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ impl Context {
|
|||
fields.push(quote! { pub #field_name: #ty });
|
||||
}
|
||||
FieldType::Struct(struct_ty) => {
|
||||
let ty = expand_struct_type(struct_ty);
|
||||
let ty = types::expand_struct_type(struct_ty);
|
||||
fields.push(quote! { pub #field_name: #ty });
|
||||
|
||||
let name = struct_ty.name();
|
||||
|
@ -592,24 +592,6 @@ fn struct_type_projections(name: &str) -> Vec<String> {
|
|||
iter.rev().map(str::to_string).collect()
|
||||
}
|
||||
|
||||
/// Expands to the rust struct type
|
||||
fn expand_struct_type(struct_ty: &StructFieldType) -> TokenStream {
|
||||
match struct_ty {
|
||||
StructFieldType::Type(ty) => {
|
||||
let ty = util::ident(&ty.name().to_pascal_case());
|
||||
quote! {#ty}
|
||||
}
|
||||
StructFieldType::Array(ty) => {
|
||||
let ty = expand_struct_type(ty);
|
||||
quote! {::std::vec::Vec<#ty>}
|
||||
}
|
||||
StructFieldType::FixedArray(ty, size) => {
|
||||
let ty = expand_struct_type(ty);
|
||||
quote! { [#ty; #size]}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
use ethers_core::{abi::ParamType, macros::ethers_core_crate};
|
||||
use eyre::{bail, Result};
|
||||
use proc_macro2::{Literal, TokenStream};
|
||||
use quote::quote;
|
||||
//! Types expansion
|
||||
|
||||
pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
|
||||
use crate::{util, InternalStructs};
|
||||
use ethers_core::{
|
||||
abi::{struct_def::StructFieldType, Event, EventParam, Param, ParamType},
|
||||
macros::ethers_core_crate,
|
||||
};
|
||||
use eyre::{bail, Result};
|
||||
use inflector::Inflector;
|
||||
use proc_macro2::{Literal, TokenStream};
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
/// Expands a ParamType Solidity type to its Rust equivalent.
|
||||
pub fn expand(kind: &ParamType) -> Result<TokenStream> {
|
||||
let ethers_core = ethers_core_crate();
|
||||
|
||||
match kind {
|
||||
|
@ -16,7 +24,7 @@ pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
|
|||
5..=8 => Ok(quote! { i64 }),
|
||||
9..=16 => Ok(quote! { i128 }),
|
||||
17..=32 => Ok(quote! { #ethers_core::types::I256 }),
|
||||
_ => bail!("unsupported solidity type int{}", n),
|
||||
_ => bail!("unsupported solidity type int{n}"),
|
||||
},
|
||||
ParamType::Uint(n) => match n / 8 {
|
||||
1 => Ok(quote! { u8 }),
|
||||
|
@ -25,41 +33,151 @@ pub(crate) fn expand(kind: &ParamType) -> Result<TokenStream> {
|
|||
5..=8 => Ok(quote! { u64 }),
|
||||
9..=16 => Ok(quote! { u128 }),
|
||||
17..=32 => Ok(quote! { #ethers_core::types::U256 }),
|
||||
_ => bail!("unsupported solidity type uint{}", n),
|
||||
_ => bail!("unsupported solidity type uint{n}"),
|
||||
},
|
||||
ParamType::Bool => Ok(quote! { bool }),
|
||||
ParamType::String => Ok(quote! { String }),
|
||||
ParamType::Array(t) => {
|
||||
let inner = expand(t)?;
|
||||
Ok(quote! { Vec<#inner> })
|
||||
}
|
||||
ParamType::FixedBytes(n) => {
|
||||
// TODO(nlordell): what is the performance impact of returning large
|
||||
// `FixedBytes` and `FixedArray`s with `web3`?
|
||||
let size = Literal::usize_unsuffixed(*n);
|
||||
Ok(quote! { [u8; #size] })
|
||||
}
|
||||
ParamType::FixedArray(t, n) => {
|
||||
// TODO(nlordell): see above
|
||||
let inner = match **t {
|
||||
ParamType::Uint(size) => {
|
||||
if size / 8 == 1 {
|
||||
ParamType::Bool => Ok(quote!(bool)),
|
||||
ParamType::String => Ok(quote!(::std::string::String)),
|
||||
ParamType::Array(ty) => Ok(array(expand(ty)?, None)),
|
||||
ParamType::FixedBytes(n) => Ok(array(quote!(u8), Some(*n))),
|
||||
ParamType::FixedArray(ty, n) => {
|
||||
let ty = match **ty {
|
||||
// this prevents type ambiguity with `FixedBytes`
|
||||
quote! { #ethers_core::types::Uint8}
|
||||
} else {
|
||||
expand(t)?
|
||||
}
|
||||
}
|
||||
_ => expand(t)?,
|
||||
// see: https://github.com/gakonst/ethers-rs/issues/1636
|
||||
ParamType::Uint(size) if size / 8 == 1 => quote!(#ethers_core::types::Uint8),
|
||||
_ => expand(ty)?,
|
||||
};
|
||||
let size = Literal::usize_unsuffixed(*n);
|
||||
Ok(quote! { [#inner; #size] })
|
||||
Ok(array(ty, Some(*n)))
|
||||
}
|
||||
ParamType::Tuple(members) => {
|
||||
eyre::ensure!(!members.is_empty(), "Tuple must have at least 1 member");
|
||||
|
||||
let members = members.iter().map(expand).collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(quote! { (#(#members,)*) })
|
||||
Ok(quote!(( #( #members ),* )))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands the event's inputs.
|
||||
pub fn expand_event_inputs(
|
||||
event: &Event,
|
||||
internal_structs: &InternalStructs,
|
||||
) -> Result<Vec<(TokenStream, TokenStream, bool)>> {
|
||||
event
|
||||
.inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, input)| {
|
||||
// NOTE: Events can contain nameless values.
|
||||
expand_event_input(input, &event.name, index, internal_structs)
|
||||
.map(|ty| (util::expand_input_name(index, &input.name), ty, input.indexed))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Expands an event property type.
|
||||
///
|
||||
/// Note that this is slightly different from expanding a Solidity type as complex types like arrays
|
||||
/// and strings get emitted as hashes when they are indexed.
|
||||
///
|
||||
/// If a complex types matches with a struct previously parsed by the internal structs, we can
|
||||
/// replace it.
|
||||
fn expand_event_input(
|
||||
input: &EventParam,
|
||||
name: &str,
|
||||
index: usize,
|
||||
internal_structs: &InternalStructs,
|
||||
) -> Result<TokenStream> {
|
||||
let kind = &input.kind;
|
||||
match (kind, input.indexed) {
|
||||
(ParamType::Array(_), true) |
|
||||
(ParamType::FixedArray(_, _), true) |
|
||||
(ParamType::Tuple(_), true) |
|
||||
(ParamType::Bytes, true) |
|
||||
(ParamType::String, true) => {
|
||||
let ethers_core = ethers_core_crate();
|
||||
Ok(quote!(#ethers_core::types::H256))
|
||||
}
|
||||
|
||||
(ParamType::Array(_), false) |
|
||||
(ParamType::FixedArray(_, _), false) |
|
||||
(ParamType::Tuple(_), false) => {
|
||||
match internal_structs.get_event_input_struct_type(name, index) {
|
||||
Some(ty) => {
|
||||
let ty = util::ident(ty);
|
||||
match kind {
|
||||
ParamType::Array(_) => Ok(array(ty, None)),
|
||||
ParamType::FixedArray(_, size) => Ok(array(ty, Some(*size))),
|
||||
ParamType::Tuple(_) => Ok(quote!(#ty)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
None => expand(kind),
|
||||
}
|
||||
}
|
||||
|
||||
_ => expand(kind),
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands `params` to `(name, type)` tokens pairs, while resolving tuples' types using the given
|
||||
/// function.
|
||||
pub fn expand_params<'a, 'b, F: Fn(&'a Param) -> Option<&'b str>>(
|
||||
params: &'a [Param],
|
||||
resolve_tuple: F,
|
||||
) -> Result<Vec<(TokenStream, TokenStream)>> {
|
||||
params
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, param)| {
|
||||
// NOTE: Params can be unnamed.
|
||||
expand_resolved(¶m.kind, param, &resolve_tuple)
|
||||
.map(|ty| (util::expand_input_name(idx, ¶m.name), ty))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Expands a ParamType Solidity type to its Rust equivalent, while resolving tuples' types using
|
||||
/// the given function.
|
||||
fn expand_resolved<'a, 'b, F: Fn(&'a Param) -> Option<&'b str>>(
|
||||
kind: &'a ParamType,
|
||||
param: &'a Param,
|
||||
resolve_tuple: &F,
|
||||
) -> Result<TokenStream> {
|
||||
match kind {
|
||||
ParamType::Array(ty) => Ok(array(expand_resolved(ty, param, resolve_tuple)?, None)),
|
||||
ParamType::FixedArray(ty, size) => {
|
||||
Ok(array(expand_resolved(ty, param, resolve_tuple)?, Some(*size)))
|
||||
}
|
||||
ParamType::Tuple(_) => match resolve_tuple(param) {
|
||||
Some(ty) => {
|
||||
let ty = util::ident(ty);
|
||||
Ok(quote!(#ty))
|
||||
}
|
||||
None => expand(kind),
|
||||
},
|
||||
_ => expand(kind),
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands to the Rust struct type.
|
||||
pub fn expand_struct_type(struct_ty: &StructFieldType) -> TokenStream {
|
||||
match struct_ty {
|
||||
StructFieldType::Type(ty) => {
|
||||
let ty = util::ident(&ty.name().to_pascal_case());
|
||||
quote!(#ty)
|
||||
}
|
||||
StructFieldType::Array(ty) => array(expand_struct_type(ty), None),
|
||||
StructFieldType::FixedArray(ty, size) => array(expand_struct_type(ty), Some(*size)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Expands `ty` into a Rust array or vector.
|
||||
fn array<T: ToTokens>(ty: T, size: Option<usize>) -> TokenStream {
|
||||
match size {
|
||||
Some(size) => {
|
||||
let size = Literal::usize_unsuffixed(size);
|
||||
quote!([#ty; #size])
|
||||
}
|
||||
None => quote!(::std::vec::Vec<#ty>),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue