Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
feat(abigen): add contract filter (#1564)
Browse files Browse the repository at this point in the history
* feat(abigen): add contract filter

* refactor: move to top of file

* add tests

* update changelog
  • Loading branch information
mattsse authored Aug 4, 2022
1 parent 3b67e0c commit e6c1927
Show file tree
Hide file tree
Showing 6 changed files with 355 additions and 91 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@

### 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)
- Support overloaded events
[#1233](https://github.com/gakonst/ethers-rs/pull/1233)
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ethers-contract/ethers-contract-abigen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ cfg-if = "1.0.0"
dunce = "1.0.2"
walkdir = "2.3.2"
eyre = "0.6"
regex = "1.6.0"

[target.'cfg(target_arch = "wasm32")'.dependencies]
# NOTE: this enables wasm compatibility for getrandom indirectly
Expand Down
174 changes: 174 additions & 0 deletions ethers-contract/ethers-contract-abigen/src/filter.rs
Original file line number Diff line number Diff line change
@@ -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);
2 changes: 2 additions & 0 deletions ethers-contract/ethers-contract-abigen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ mod rustfmt;
mod source;
mod util;

pub mod filter;
pub use filter::{ContractFilter, ExcludeContracts, SelectContracts};
pub mod multi;
pub use multi::MultiAbigen;

Expand Down
Loading

0 comments on commit e6c1927

Please sign in to comment.