feat(abigen): add contract filter (#1564)
* feat(abigen): add contract filter * refactor: move to top of file * add tests * update changelog
This commit is contained in:
parent
3b67e0c560
commit
e6c1927a1c
|
@ -87,6 +87,7 @@
|
||||||
|
|
||||||
### Unreleased
|
### Unreleased
|
||||||
|
|
||||||
|
- Add `ContractFilter` to filter contracts in `MultiAbigen` [#1564](https://github.com/gakonst/ethers-rs/pull/1564)
|
||||||
- generate error bindings for custom errors [#1549](https://github.com/gakonst/ethers-rs/pull/1549)
|
- generate error bindings for custom errors [#1549](https://github.com/gakonst/ethers-rs/pull/1549)
|
||||||
- Support overloaded events
|
- Support overloaded events
|
||||||
[#1233](https://github.com/gakonst/ethers-rs/pull/1233)
|
[#1233](https://github.com/gakonst/ethers-rs/pull/1233)
|
||||||
|
|
|
@ -1259,6 +1259,7 @@ dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
@ -25,6 +25,7 @@ cfg-if = "1.0.0"
|
||||||
dunce = "1.0.2"
|
dunce = "1.0.2"
|
||||||
walkdir = "2.3.2"
|
walkdir = "2.3.2"
|
||||||
eyre = "0.6"
|
eyre = "0.6"
|
||||||
|
regex = "1.6.0"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
# NOTE: this enables wasm compatibility for getrandom indirectly
|
# NOTE: this enables wasm compatibility for getrandom indirectly
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
//! Filtering support for contracts used in [`Abigen`]
|
||||||
|
|
||||||
|
use regex::bytes::Regex;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
/// Used to filter contracts that should be _included_ in the abigen generation.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ContractFilter {
|
||||||
|
/// Include all contracts
|
||||||
|
All,
|
||||||
|
/// Only include contracts that match the filter
|
||||||
|
Select(SelectContracts),
|
||||||
|
/// Only include contracts that _don't_ match the filter
|
||||||
|
Exclude(ExcludeContracts),
|
||||||
|
}
|
||||||
|
|
||||||
|
// === impl ContractFilter ===
|
||||||
|
|
||||||
|
impl ContractFilter {
|
||||||
|
/// Returns whether to include the contract with the given `name`
|
||||||
|
pub fn is_match(&self, name: impl AsRef<str>) -> bool {
|
||||||
|
match self {
|
||||||
|
ContractFilter::All => true,
|
||||||
|
ContractFilter::Select(f) => f.is_match(name),
|
||||||
|
ContractFilter::Exclude(f) => !f.is_match(name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ContractFilter {
|
||||||
|
fn default() -> Self {
|
||||||
|
ContractFilter::All
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SelectContracts> for ContractFilter {
|
||||||
|
fn from(f: SelectContracts) -> Self {
|
||||||
|
ContractFilter::Select(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ExcludeContracts> for ContractFilter {
|
||||||
|
fn from(f: ExcludeContracts) -> Self {
|
||||||
|
ContractFilter::Exclude(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_filter {
|
||||||
|
($name:ident) => {
|
||||||
|
impl $name {
|
||||||
|
/// Adds an exact name to the filter
|
||||||
|
pub fn add_name<T: Into<String>>(mut self, arg: T) -> Self {
|
||||||
|
self.exact.insert(arg.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds multiple exact names to the filter
|
||||||
|
pub fn extend_names<I, S>(mut self, name: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
for arg in name {
|
||||||
|
self = self.add_name(arg);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds the regex to use
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `pattern` is an invalid `Regex`
|
||||||
|
pub fn add_regex(mut self, re: Regex) -> Self {
|
||||||
|
self.patterns.push(re);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds multiple exact names to the filter
|
||||||
|
pub fn extend_regex<I, S>(mut self, regexes: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: Into<Regex>,
|
||||||
|
{
|
||||||
|
for re in regexes {
|
||||||
|
self = self.add_regex(re.into());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the pattern to use
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `pattern` is an invalid `Regex`
|
||||||
|
pub fn add_pattern(self, pattern: impl AsRef<str>) -> Self {
|
||||||
|
self.try_add_pattern(pattern).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the pattern to use
|
||||||
|
pub fn try_add_pattern(mut self, s: impl AsRef<str>) -> Result<Self, regex::Error> {
|
||||||
|
self.patterns.push(Regex::new(s.as_ref())?);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds multiple patterns to the filter
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `pattern` is an invalid `Regex`
|
||||||
|
pub fn extend_pattern<I, S>(self, patterns: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: AsRef<str>,
|
||||||
|
{
|
||||||
|
self.try_extend_pattern(patterns).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds multiple patterns to the filter
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `pattern` is an invalid `Regex`
|
||||||
|
pub fn try_extend_pattern<I, S>(mut self, patterns: I) -> Result<Self, regex::Error>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: AsRef<str>,
|
||||||
|
{
|
||||||
|
for p in patterns {
|
||||||
|
self = self.try_add_pattern(p)?;
|
||||||
|
}
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true whether the `name` matches the filter
|
||||||
|
pub fn is_match(&self, name: impl AsRef<str>) -> bool {
|
||||||
|
let name = name.as_ref();
|
||||||
|
if self.exact.contains(name) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
self.patterns.iter().any(|re| re.is_match(name.as_bytes()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Contract Filter that only includes certain contracts.
|
||||||
|
///
|
||||||
|
/// **Note:**: matching by exact name and via regex stacks
|
||||||
|
///
|
||||||
|
/// This is the inverse of `ExcludeContracts`
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct SelectContracts {
|
||||||
|
/// Include contracts based on their exact name
|
||||||
|
exact: HashSet<String>,
|
||||||
|
/// Include contracts if their name matches a pattern
|
||||||
|
patterns: Vec<Regex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Contract Filter that exclude certain contracts
|
||||||
|
///
|
||||||
|
/// **Note:**: matching by exact name and via regex stacks
|
||||||
|
///
|
||||||
|
/// This is the inverse of `SelectContracts`
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct ExcludeContracts {
|
||||||
|
/// Exclude contracts based on their exact name
|
||||||
|
exact: HashSet<String>,
|
||||||
|
/// Exclude contracts if their name matches any pattern
|
||||||
|
patterns: Vec<Regex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_filter!(SelectContracts);
|
||||||
|
impl_filter!(ExcludeContracts);
|
|
@ -20,6 +20,8 @@ mod rustfmt;
|
||||||
mod source;
|
mod source;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
pub mod filter;
|
||||||
|
pub use filter::{ContractFilter, ExcludeContracts, SelectContracts};
|
||||||
pub mod multi;
|
pub mod multi;
|
||||||
pub use multi::MultiAbigen;
|
pub use multi::MultiAbigen;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
//! TODO
|
//! Generate bindings for multiple `Abigen`
|
||||||
|
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use inflector::Inflector;
|
use inflector::Inflector;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
|
@ -11,7 +10,131 @@ use std::{
|
||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{util, Abigen, Context, ContractBindings, ExpandedContract};
|
use crate::{util, Abigen, Context, ContractBindings, ContractFilter, ExpandedContract};
|
||||||
|
|
||||||
|
/// Collects Abigen structs for a series of contracts, pending generation of
|
||||||
|
/// the contract bindings.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MultiAbigen {
|
||||||
|
/// Abigen objects to be written
|
||||||
|
abigens: Vec<Abigen>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for MultiAbigen {
|
||||||
|
type Target = Vec<Abigen>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.abigens
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Abigen>> for MultiAbigen {
|
||||||
|
fn from(abigens: Vec<Abigen>) -> Self {
|
||||||
|
Self { abigens }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::iter::FromIterator<Abigen> for MultiAbigen {
|
||||||
|
fn from_iter<I: IntoIterator<Item = Abigen>>(iter: I) -> Self {
|
||||||
|
iter.into_iter().collect::<Vec<_>>().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MultiAbigen {
|
||||||
|
/// Create a new instance from a series (`contract name`, `abi_source`)
|
||||||
|
///
|
||||||
|
/// See `Abigen::new`
|
||||||
|
pub fn new<I, Name, Source>(abis: I) -> Result<Self>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (Name, Source)>,
|
||||||
|
Name: AsRef<str>,
|
||||||
|
Source: AsRef<str>,
|
||||||
|
{
|
||||||
|
let abis = abis
|
||||||
|
.into_iter()
|
||||||
|
.map(|(contract_name, abi_source)| Abigen::new(contract_name.as_ref(), abi_source))
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
Ok(Self::from_abigens(abis))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new instance from a series of already resolved `Abigen`
|
||||||
|
pub fn from_abigens(abis: impl IntoIterator<Item = Abigen>) -> Self {
|
||||||
|
abis.into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads all json files contained in the given `dir` and use the file name for the name of the
|
||||||
|
/// `ContractBindings`.
|
||||||
|
/// This is equivalent to calling `MultiAbigen::new` with all the json files and their filename.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// abi
|
||||||
|
/// ├── ERC20.json
|
||||||
|
/// ├── Contract1.json
|
||||||
|
/// ├── Contract2.json
|
||||||
|
/// ...
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # fn t() {
|
||||||
|
/// # use ethers_contract_abigen::MultiAbigen;
|
||||||
|
/// let gen = MultiAbigen::from_json_files("./abi").unwrap();
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn from_json_files(root: impl AsRef<Path>) -> Result<Self> {
|
||||||
|
util::json_files(root.as_ref()).into_iter().map(Abigen::from_file).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See `apply_filter`
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// Only Select specific contracts
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ethers_contract_abigen::{MultiAbigen, SelectContracts};
|
||||||
|
/// # fn t() {
|
||||||
|
/// let gen = MultiAbigen::from_json_files("./abi").unwrap().with_filter(
|
||||||
|
/// SelectContracts::default().add_name("MyContract").add_name("MyOtherContract"),
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Exclude all contracts that end with test
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ethers_contract_abigen::{ExcludeContracts, MultiAbigen};
|
||||||
|
/// # fn t() {
|
||||||
|
/// let gen = MultiAbigen::from_json_files("./abi").unwrap().with_filter(
|
||||||
|
/// ExcludeContracts::default().add_pattern(".*Test"),
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_filter(mut self, filter: impl Into<ContractFilter>) -> Self {
|
||||||
|
self.apply_filter(&filter.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes all `Abigen` items that should not be included based on the given filter
|
||||||
|
pub fn apply_filter(&mut self, filter: &ContractFilter) {
|
||||||
|
self.abigens.retain(|abi| filter.is_match(&abi.contract_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add another Abigen to the module or lib
|
||||||
|
pub fn push(&mut self, abigen: Abigen) {
|
||||||
|
self.abigens.push(abigen)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the contract bindings and prepare for writing
|
||||||
|
pub fn build(self) -> Result<MultiBindings> {
|
||||||
|
let rustfmt = self.abigens.iter().any(|gen| gen.rustfmt);
|
||||||
|
Ok(MultiBindings {
|
||||||
|
expansion: MultiExpansion::from_abigen(self.abigens)?.expand(),
|
||||||
|
rustfmt,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a collection of [`Abigen::expand()`]
|
/// Represents a collection of [`Abigen::expand()`]
|
||||||
pub struct MultiExpansion {
|
pub struct MultiExpansion {
|
||||||
|
@ -189,94 +312,6 @@ impl MultiExpansionResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collects Abigen structs for a series of contracts, pending generation of
|
|
||||||
/// the contract bindings.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct MultiAbigen {
|
|
||||||
/// Abigen objects to be written
|
|
||||||
abigens: Vec<Abigen>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for MultiAbigen {
|
|
||||||
type Target = Vec<Abigen>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.abigens
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<Abigen>> for MultiAbigen {
|
|
||||||
fn from(abigens: Vec<Abigen>) -> Self {
|
|
||||||
Self { abigens }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::iter::FromIterator<Abigen> for MultiAbigen {
|
|
||||||
fn from_iter<I: IntoIterator<Item = Abigen>>(iter: I) -> Self {
|
|
||||||
iter.into_iter().collect::<Vec<_>>().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MultiAbigen {
|
|
||||||
/// Create a new instance from a series (`contract name`, `abi_source`)
|
|
||||||
///
|
|
||||||
/// See `Abigen::new`
|
|
||||||
pub fn new<I, Name, Source>(abis: I) -> Result<Self>
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = (Name, Source)>,
|
|
||||||
Name: AsRef<str>,
|
|
||||||
Source: AsRef<str>,
|
|
||||||
{
|
|
||||||
let abis = abis
|
|
||||||
.into_iter()
|
|
||||||
.map(|(contract_name, abi_source)| Abigen::new(contract_name.as_ref(), abi_source))
|
|
||||||
.collect::<Result<Vec<_>>>()?;
|
|
||||||
|
|
||||||
Ok(Self::from_abigens(abis))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new instance from a series of already resolved `Abigen`
|
|
||||||
pub fn from_abigens(abis: impl IntoIterator<Item = Abigen>) -> Self {
|
|
||||||
abis.into_iter().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads all json files contained in the given `dir` and use the file name for the name of the
|
|
||||||
/// `ContractBindings`.
|
|
||||||
/// This is equivalent to calling `MultiAbigen::new` with all the json files and their filename.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```text
|
|
||||||
/// abi
|
|
||||||
/// ├── ERC20.json
|
|
||||||
/// ├── Contract1.json
|
|
||||||
/// ├── Contract2.json
|
|
||||||
/// ...
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// # use ethers_contract_abigen::MultiAbigen;
|
|
||||||
/// let gen = MultiAbigen::from_json_files("./abi").unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn from_json_files(root: impl AsRef<Path>) -> Result<Self> {
|
|
||||||
util::json_files(root.as_ref()).into_iter().map(Abigen::from_file).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add another Abigen to the module or lib
|
|
||||||
pub fn push(&mut self, abigen: Abigen) {
|
|
||||||
self.abigens.push(abigen)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build the contract bindings and prepare for writing
|
|
||||||
pub fn build(self) -> Result<MultiBindings> {
|
|
||||||
let rustfmt = self.abigens.iter().any(|gen| gen.rustfmt);
|
|
||||||
Ok(MultiBindings {
|
|
||||||
expansion: MultiExpansion::from_abigen(self.abigens)?.expand(),
|
|
||||||
rustfmt,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Output of the [`MultiAbigen`] build process. `MultiBindings` wraps a group
|
/// Output of the [`MultiAbigen`] build process. `MultiBindings` wraps a group
|
||||||
/// of built contract bindings that have yet to be written to disk.
|
/// of built contract bindings that have yet to be written to disk.
|
||||||
///
|
///
|
||||||
|
@ -736,6 +771,7 @@ fn check_binding_in_dir(dir: &Path, binding: &ContractBindings) -> Result<()> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use crate::{ExcludeContracts, SelectContracts};
|
||||||
use ethers_solc::project_util::TempProject;
|
use ethers_solc::project_util::TempProject;
|
||||||
use std::{panic, path::PathBuf};
|
use std::{panic, path::PathBuf};
|
||||||
|
|
||||||
|
@ -1015,6 +1051,55 @@ mod tests {
|
||||||
assert!(!tokens.contains("mod __shared_types"));
|
assert!(!tokens.contains("mod __shared_types"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_filter_abigen() {
|
||||||
|
let abi = Abigen::new(
|
||||||
|
"MyGreeter",
|
||||||
|
r#"[
|
||||||
|
greet() (string)
|
||||||
|
]"#,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut gen = MultiAbigen::from_abigens(vec![abi]).with_filter(ContractFilter::All);
|
||||||
|
assert_eq!(gen.abigens.len(), 1);
|
||||||
|
gen.apply_filter(&SelectContracts::default().add_name("MyGreeter").into());
|
||||||
|
assert_eq!(gen.abigens.len(), 1);
|
||||||
|
|
||||||
|
gen.apply_filter(&ExcludeContracts::default().add_name("MyGreeter2").into());
|
||||||
|
assert_eq!(gen.abigens.len(), 1);
|
||||||
|
|
||||||
|
let filtered = gen.clone().with_filter(SelectContracts::default().add_name("MyGreeter2"));
|
||||||
|
assert!(filtered.abigens.is_empty());
|
||||||
|
|
||||||
|
let filtered = gen.clone().with_filter(ExcludeContracts::default().add_name("MyGreeter"));
|
||||||
|
assert!(filtered.abigens.is_empty());
|
||||||
|
|
||||||
|
let filtered =
|
||||||
|
gen.clone().with_filter(SelectContracts::default().add_pattern("MyGreeter2"));
|
||||||
|
assert!(filtered.abigens.is_empty());
|
||||||
|
|
||||||
|
let filtered =
|
||||||
|
gen.clone().with_filter(ExcludeContracts::default().add_pattern("MyGreeter"));
|
||||||
|
assert!(filtered.abigens.is_empty());
|
||||||
|
|
||||||
|
gen.push(
|
||||||
|
Abigen::new(
|
||||||
|
"MyGreeterTest",
|
||||||
|
r#"[
|
||||||
|
greet() (string)
|
||||||
|
]"#,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let filtered = gen.clone().with_filter(SelectContracts::default().add_pattern(".*Test"));
|
||||||
|
assert_eq!(filtered.abigens.len(), 1);
|
||||||
|
assert_eq!(filtered.abigens[0].contract_name, "MyGreeterTest");
|
||||||
|
|
||||||
|
let filtered = gen.clone().with_filter(ExcludeContracts::default().add_pattern(".*Test"));
|
||||||
|
assert_eq!(filtered.abigens.len(), 1);
|
||||||
|
assert_eq!(filtered.abigens[0].contract_name, "MyGreeter");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_deduplicate_types() {
|
fn can_deduplicate_types() {
|
||||||
let tmp = TempProject::dapptools().unwrap();
|
let tmp = TempProject::dapptools().unwrap();
|
||||||
|
|
Loading…
Reference in New Issue