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

refactor(solc): unify find/remove api #1449

Merged
merged 2 commits into from
Jul 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
49 changes: 45 additions & 4 deletions ethers-solc/src/artifact_output/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ use std::{
borrow::Cow,
collections::{btree_map::BTreeMap, HashSet},
fmt, fs, io,
ops::Deref,
path::{Path, PathBuf},
};

mod configurable;
pub use configurable::*;

Expand Down Expand Up @@ -64,7 +66,7 @@ impl ArtifactId {
}

/// Represents an artifact file representing a [`crate::Contract`]
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArtifactFile<T> {
/// The Artifact that was written
pub artifact: T,
Expand Down Expand Up @@ -102,7 +104,7 @@ impl<T> ArtifactFile<T> {
pub(crate) type ArtifactsMap<T> = FileToContractsMap<Vec<ArtifactFile<T>>>;

/// Represents a set of Artifacts
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Artifacts<T>(pub ArtifactsMap<T>);

impl<T> From<ArtifactsMap<T>> for Artifacts<T> {
Expand Down Expand Up @@ -149,6 +151,14 @@ impl<T> AsMut<ArtifactsMap<T>> for Artifacts<T> {
}
}

impl<T> Deref for Artifacts<T> {
type Target = ArtifactsMap<T>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T: Serialize> Artifacts<T> {
/// Writes all artifacts into the given `artifacts_root` folder
pub fn write_all(&self) -> Result<()> {
Expand Down Expand Up @@ -264,18 +274,49 @@ impl<T> Artifacts<T> {
}

/// Finds the first artifact `T` with a matching contract name
pub fn find(&self, contract_name: impl AsRef<str>) -> Option<&T> {
pub fn find_first(&self, contract_name: impl AsRef<str>) -> Option<&T> {
let contract_name = contract_name.as_ref();
self.0.iter().find_map(|(_file, contracts)| {
contracts.get(contract_name).and_then(|c| c.get(0).map(|a| &a.artifact))
})
}

/// Finds the artifact with matching path and name
pub fn find(&self, path: impl AsRef<str>, contract: impl AsRef<str>) -> Option<&T> {
let contract_path = path.as_ref();
let contract_name = contract.as_ref();
self.0.iter().filter(|(path, _)| path.as_str() == contract_path).find_map(
|(_file, contracts)| {
contracts.get(contract_name).and_then(|c| c.get(0).map(|a| &a.artifact))
},
)
}

/// Removes the artifact with matching file and name
pub fn remove(&mut self, path: impl AsRef<str>, contract: impl AsRef<str>) -> Option<T> {
let contract_path = path.as_ref();
let contract_name = contract.as_ref();
self.0.iter_mut().filter(|(path, _)| path.as_str() == contract_path).find_map(
|(_file, contracts)| {
let mut artifact = None;
if let Some((c, mut artifacts)) = contracts.remove_entry(contract_name) {
if !artifacts.is_empty() {
artifact = Some(artifacts.remove(0).artifact);
}
if !artifacts.is_empty() {
contracts.insert(c, artifacts);
}
}
artifact
},
)
}

/// Removes the first artifact `T` with a matching contract name
///
/// *Note:* if there are multiple artifacts (contract compiled with different solc) then this
/// returns the first artifact in that set
pub fn remove(&mut self, contract_name: impl AsRef<str>) -> Option<T> {
pub fn remove_first(&mut self, contract_name: impl AsRef<str>) -> Option<T> {
let contract_name = contract_name.as_ref();
self.0.iter_mut().find_map(|(_file, contracts)| {
let mut artifact = None;
Expand Down
2 changes: 1 addition & 1 deletion ethers-solc/src/artifacts/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ impl<'a> CompactContractRef<'a> {
/// use ethers_solc::artifacts::*;
/// # fn demo(project: Project) {
/// let output = project.compile().unwrap().output();
/// let contract = output.find("Greeter").unwrap();
/// let contract = output.find_first("Greeter").unwrap();
/// let contract = contract.unwrap();
/// # }
/// ```
Expand Down
39 changes: 36 additions & 3 deletions ethers-solc/src/compile/output/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::artifacts::{
};
use semver::Version;
use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, path::Path};
use std::{collections::BTreeMap, ops::Deref, path::Path};

/// file -> [(contract name -> Contract + solc version)]
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
Expand Down Expand Up @@ -34,16 +34,41 @@ impl VersionedContracts {
/// use ethers_solc::artifacts::*;
/// # fn demo(project: Project) {
/// let output = project.compile().unwrap().output();
/// let contract = output.find("Greeter").unwrap();
/// let contract = output.find_first("Greeter").unwrap();
/// # }
/// ```
pub fn find(&self, contract: impl AsRef<str>) -> Option<CompactContractRef> {
pub fn find_first(&self, contract: impl AsRef<str>) -> Option<CompactContractRef> {
let contract_name = contract.as_ref();
self.contracts().find_map(|(name, contract)| {
(name == contract_name).then(|| CompactContractRef::from(contract))
})
}

/// Finds the contract with matching path and name
///
/// # Example
///
/// ```
/// use ethers_solc::Project;
/// use ethers_solc::artifacts::*;
/// # fn demo(project: Project) {
/// let output = project.compile().unwrap().output();
/// let contract = output.contracts.find("src/Greeter.sol", "Greeter").unwrap();
/// # }
/// ```
pub fn find(
&self,
path: impl AsRef<str>,
contract: impl AsRef<str>,
) -> Option<CompactContractRef> {
let contract_path = path.as_ref();
let contract_name = contract.as_ref();
self.contracts_with_files().find_map(|(path, name, contract)| {
(path == contract_path && name == contract_name)
.then(|| CompactContractRef::from(contract))
})
}

/// Removes the _first_ contract with the given name from the set
///
/// # Example
Expand Down Expand Up @@ -231,6 +256,14 @@ impl AsMut<FileToContractsMap<Vec<VersionedContract>>> for VersionedContracts {
}
}

impl Deref for VersionedContracts {
type Target = FileToContractsMap<Vec<VersionedContract>>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl IntoIterator for VersionedContracts {
type Item = (String, BTreeMap<String, Vec<VersionedContract>>);
type IntoIter =
Expand Down
176 changes: 155 additions & 21 deletions ethers-solc/src/compile/output/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,28 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
cached_artifacts.into_artifacts::<T>().chain(compiled_artifacts.into_artifacts::<T>())
}

/// This returns a chained iterator of both cached and recompiled contract artifacts that yields
/// the contract name and the corresponding artifact
///
/// # Example
///
/// ```no_run
/// use std::collections::btree_map::BTreeMap;
/// use ethers_solc::ConfigurableContractArtifact;
/// use ethers_solc::Project;
///
/// let project = Project::builder().build().unwrap();
/// let artifacts: BTreeMap<String, &ConfigurableContractArtifact> = project.compile().unwrap().artifacts().collect();
/// ```
pub fn artifacts(&self) -> impl Iterator<Item = (String, &T::Artifact)> {
self.cached_artifacts
.artifact_files()
.chain(self.compiled_artifacts.artifact_files())
.filter_map(|artifact| {
T::contract_name(&artifact.file).map(|name| (name, &artifact.artifact))
})
}

/// All artifacts together with their contract file and name as tuple `(file, contract
/// name, artifact)`
///
Expand Down Expand Up @@ -151,15 +173,6 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
self.compiler_output.has_warning(&self.ignored_error_codes)
}

/// Finds the first contract with the given name and removes it from the set
pub fn remove(&mut self, contract_name: impl AsRef<str>) -> Option<T::Artifact> {
let contract_name = contract_name.as_ref();
if let artifact @ Some(_) = self.compiled_artifacts.remove(contract_name) {
return artifact
}
self.cached_artifacts.remove(contract_name)
}

/// Returns the set of `Artifacts` that were cached and got reused during
/// [`crate::Project::compile()`]
pub fn cached_artifacts(&self) -> &Artifacts<T::Artifact> {
Expand Down Expand Up @@ -188,19 +201,140 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
}
contracts
}
}

impl<T: ArtifactOutput> ProjectCompileOutput<T>
where
T::Artifact: Clone,
{
/// Removes the contract with matching path and name using the `<path>:<contractname>` pattern
/// where `path` is optional.
///
/// If the `path` segment is `None`, then the first matching `Contract` is returned, see
/// [Self::remove_first]
///
/// # Example
///
///
/// ```
/// use ethers_solc::Project;
/// use ethers_solc::artifacts::*;
/// use ethers_solc::info::ContractInfo;
///
/// # fn demo(project: Project) {
/// let output = project.compile().unwrap();
/// let info = ContractInfo::new("src/Greeter.sol:Greeter");
/// let contract = output.find_contract(&info).unwrap();
/// # }
/// ```
pub fn find_contract<'a>(&self, info: impl Into<ContractInfoRef<'a>>) -> Option<&T::Artifact> {
let ContractInfoRef { path, name } = info.into();
if let Some(path) = path {
self.find(path, name)
} else {
self.find_first(name)
}
}

/// Finds the artifact with matching path and name
///
/// # Example
///
/// ```
/// use ethers_solc::Project;
/// use ethers_solc::artifacts::*;
/// # fn demo(project: Project) {
/// let output = project.compile().unwrap();
/// let contract = output.find("src/Greeter.sol", "Greeter").unwrap();
/// # }
/// ```
pub fn find(&self, path: impl AsRef<str>, contract: impl AsRef<str>) -> Option<&T::Artifact> {
let contract_path = path.as_ref();
let contract_name = contract.as_ref();
if let artifact @ Some(_) = self.compiled_artifacts.find(contract_path, contract_name) {
return artifact
}
self.cached_artifacts.find(contract_path, contract_name)
}

/// Finds the first contract with the given name
pub fn find(&self, contract_name: impl AsRef<str>) -> Option<&T::Artifact> {
pub fn find_first(&self, contract_name: impl AsRef<str>) -> Option<&T::Artifact> {
let contract_name = contract_name.as_ref();
if let artifact @ Some(_) = self.compiled_artifacts.find_first(contract_name) {
return artifact
}
self.cached_artifacts.find_first(contract_name)
}

/// Finds the artifact with matching path and name
///
/// # Example
///
/// ```
/// use ethers_solc::Project;
/// use ethers_solc::artifacts::*;
/// # fn demo(project: Project) {
/// let output = project.compile().unwrap();
/// let contract = output.find("src/Greeter.sol", "Greeter").unwrap();
/// # }
/// ```
pub fn remove(
&mut self,
path: impl AsRef<str>,
contract: impl AsRef<str>,
) -> Option<T::Artifact> {
let contract_path = path.as_ref();
let contract_name = contract.as_ref();
if let artifact @ Some(_) = self.compiled_artifacts.remove(contract_path, contract_name) {
return artifact
}
self.cached_artifacts.remove(contract_path, contract_name)
}

/// Removes the _first_ contract with the given name from the set
///
/// # Example
///
/// ```
/// use ethers_solc::Project;
/// use ethers_solc::artifacts::*;
/// # fn demo(project: Project) {
/// let mut output = project.compile().unwrap();
/// let contract = output.remove_first("Greeter").unwrap();
/// # }
/// ```
pub fn remove_first(&mut self, contract_name: impl AsRef<str>) -> Option<T::Artifact> {
let contract_name = contract_name.as_ref();
if let artifact @ Some(_) = self.compiled_artifacts.find(contract_name) {
if let artifact @ Some(_) = self.compiled_artifacts.remove_first(contract_name) {
return artifact
}
self.cached_artifacts.find(contract_name)
self.cached_artifacts.remove_first(contract_name)
}

/// Removes the contract with matching path and name using the `<path>:<contractname>` pattern
/// where `path` is optional.
///
/// If the `path` segment is `None`, then the first matching `Contract` is returned, see
/// [Self::remove_first]
///
///
/// # Example
///
/// ```
/// use ethers_solc::Project;
/// use ethers_solc::artifacts::*;
/// use ethers_solc::info::ContractInfo;
/// # fn demo(project: Project) {
/// let mut output = project.compile().unwrap();
/// let info = ContractInfo::new("src/Greeter.sol:Greeter");
/// let contract = output.remove_contract(&info).unwrap();
/// # }
/// ```
pub fn remove_contract<'a>(
&mut self,
info: impl Into<ContractInfoRef<'a>>,
) -> Option<T::Artifact> {
let ContractInfoRef { path, name } = info.into();
if let Some(path) = path {
self.remove(path, name)
} else {
self.remove_first(name)
}
}
}

Expand Down Expand Up @@ -340,11 +474,11 @@ impl AggregatedCompilerOutput {
/// use ethers_solc::artifacts::*;
/// # fn demo(project: Project) {
/// let output = project.compile().unwrap().output();
/// let contract = output.find("Greeter").unwrap();
/// let contract = output.find_first("Greeter").unwrap();
/// # }
/// ```
pub fn find(&self, contract: impl AsRef<str>) -> Option<CompactContractRef> {
self.contracts.find(contract)
pub fn find_first(&self, contract: impl AsRef<str>) -> Option<CompactContractRef> {
self.contracts.find_first(contract)
}

/// Removes the _first_ contract with the given name from the set
Expand Down Expand Up @@ -548,7 +682,7 @@ impl<'a> OutputDiagnostics<'a> {
return true
}

self.compiler_output.find(&contract_path).map_or(false, |contract| {
self.compiler_output.find_first(&contract_path).map_or(false, |contract| {
contract.abi.map_or(false, |abi| abi.functions.contains_key("IS_TEST"))
})
}
Expand Down
Loading