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

feat(abigen): add contract filter #1564

Merged
merged 4 commits into from
Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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