feat: enumerate overloaded functions if they are nameless (#545)
* fix: overloaded with same type arguments * fix: numerate overloaded functions * docs: add note * chore: update changelog * style: rename vars
This commit is contained in:
parent
46bedb3282
commit
4123823383
|
@ -4,6 +4,7 @@
|
|||
|
||||
### Unreleased
|
||||
|
||||
- use enumerated aliases for overloaded functions [#545](https://github.com/gakonst/ethers-rs/pull/545)
|
||||
- move `AbiEncode` `AbiDecode` trait to ethers-core and implement for core types [#531](https://github.com/gakonst/ethers-rs/pull/531)
|
||||
- add `EthCall` trait and derive macro which generates matching structs for contract calls [#517](https://github.com/gakonst/ethers-rs/pull/517)
|
||||
- `abigen!` now generates `Display` for all events using the new `EthDisplay` macro [#513](https://github.com/gakonst/ethers-rs/pull/513)
|
||||
|
|
|
@ -282,15 +282,27 @@ impl Context {
|
|||
// sort functions by number of inputs asc
|
||||
let mut functions = functions.iter().collect::<Vec<_>>();
|
||||
functions.sort_by(|f1, f2| f1.inputs.len().cmp(&f2.inputs.len()));
|
||||
let prev = functions[0];
|
||||
for duplicate in functions.into_iter().skip(1) {
|
||||
let first = functions[0];
|
||||
// assuming here that if there are overloaded functions with nameless params like `log;,
|
||||
// log(string); log(string, string)` `log()` should also be aliased with its
|
||||
// index to `log0`
|
||||
let mut add_alias_for_first_with_idx = false;
|
||||
for (idx, duplicate) in functions.into_iter().enumerate().skip(1) {
|
||||
// attempt to find diff in the input arguments
|
||||
let diff = duplicate
|
||||
.inputs
|
||||
.iter()
|
||||
.filter(|i1| prev.inputs.iter().all(|i2| *i1 != i2))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut diff = Vec::new();
|
||||
let mut same_params = true;
|
||||
for (idx, i1) in duplicate.inputs.iter().enumerate() {
|
||||
if first.inputs.iter().all(|i2| i1 != i2) {
|
||||
diff.push(i1);
|
||||
same_params = false;
|
||||
} else {
|
||||
// check for cases like `log(string); log(string, string)` by keep track of
|
||||
// same order
|
||||
if same_params && idx + 1 > first.inputs.len() {
|
||||
diff.push(i1);
|
||||
}
|
||||
}
|
||||
}
|
||||
let alias = match diff.len() {
|
||||
0 => {
|
||||
// this should not happen since functions with same name and input are
|
||||
|
@ -302,30 +314,46 @@ impl Context {
|
|||
}
|
||||
1 => {
|
||||
// single additional input params
|
||||
format!(
|
||||
"{}_with_{}",
|
||||
duplicate.name.to_snake_case(),
|
||||
diff[0].name.to_snake_case()
|
||||
)
|
||||
if diff[0].name.is_empty() {
|
||||
add_alias_for_first_with_idx = true;
|
||||
format!("{}1", duplicate.name.to_snake_case())
|
||||
} else {
|
||||
format!(
|
||||
"{}_with_{}",
|
||||
duplicate.name.to_snake_case(),
|
||||
diff[0].name.to_snake_case()
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// 1 + n additional input params
|
||||
let and = diff
|
||||
.iter()
|
||||
.skip(1)
|
||||
.map(|i| i.name.to_snake_case())
|
||||
.collect::<Vec<_>>()
|
||||
.join("_and_");
|
||||
format!(
|
||||
"{}_with_{}_and_{}",
|
||||
duplicate.name.to_snake_case(),
|
||||
diff[0].name.to_snake_case(),
|
||||
and
|
||||
)
|
||||
if diff.iter().any(|d| d.name.is_empty()) {
|
||||
add_alias_for_first_with_idx = true;
|
||||
format!("{}{}", duplicate.name.to_snake_case(), idx)
|
||||
} else {
|
||||
// 1 + n additional input params
|
||||
let and = diff
|
||||
.iter()
|
||||
.skip(1)
|
||||
.map(|i| i.name.to_snake_case())
|
||||
.collect::<Vec<_>>()
|
||||
.join("_and_");
|
||||
format!(
|
||||
"{}_with_{}_and_{}",
|
||||
duplicate.name.to_snake_case(),
|
||||
diff[0].name.to_snake_case(),
|
||||
and
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
aliases.insert(duplicate.abi_signature(), util::safe_ident(&alias));
|
||||
}
|
||||
|
||||
if add_alias_for_first_with_idx {
|
||||
// insert an alias for the root duplicated call
|
||||
let prev_alias = format!("{}0", first.name.to_snake_case());
|
||||
aliases.insert(first.abi_signature(), util::safe_ident(&prev_alias));
|
||||
}
|
||||
}
|
||||
Ok(aliases)
|
||||
}
|
||||
|
|
|
@ -62,6 +62,9 @@ pub(crate) mod utils;
|
|||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// Aliases for overloaded functions with no aliases provided in the `method` section are derived
|
||||
/// automatically.
|
||||
///
|
||||
/// `abigen!` supports multiple abigen definitions separated by a semicolon `;`
|
||||
/// This is useful if the contracts use ABIEncoderV2 structs. In which case
|
||||
/// `abigen!` bundles all type duplicates so that all rust contracts also use
|
||||
|
|
|
@ -191,6 +191,8 @@ fn can_handle_overloaded_functions() {
|
|||
getValue() (uint256)
|
||||
getValue(uint256 otherValue) (uint256)
|
||||
getValue(uint256 otherValue, address addr) (uint256)
|
||||
setValue(string, string)
|
||||
setValue(string)
|
||||
]"#
|
||||
);
|
||||
|
||||
|
@ -238,6 +240,30 @@ fn can_handle_overloaded_functions() {
|
|||
let decoded_enum = SimpleContractCalls::decode(encoded_call.as_ref()).unwrap();
|
||||
assert_eq!(contract_call, decoded_enum);
|
||||
assert_eq!(encoded_call, contract_call.encode().into());
|
||||
|
||||
let call = SetValue0Call("message".to_string());
|
||||
let _contract_call = SimpleContractCalls::SetValue0(call);
|
||||
let call = SetValue1Call("message".to_string(), "message".to_string());
|
||||
let _contract_call = SimpleContractCalls::SetValue1(call);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_handle_even_more_overloaded_functions() {
|
||||
abigen!(
|
||||
ConsoleLog,
|
||||
r#"[
|
||||
log()
|
||||
log(string, string)
|
||||
log(string)
|
||||
]"#
|
||||
);
|
||||
|
||||
let _call = Log0Call;
|
||||
let _contract_call = ConsoleLogCalls::Log0;
|
||||
let call = Log1Call("message".to_string());
|
||||
let _contract_call = ConsoleLogCalls::Log1(call);
|
||||
let call = Log2Call("message".to_string(), "message".to_string());
|
||||
let _contract_call = ConsoleLogCalls::Log2(call);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
Loading…
Reference in New Issue