Artifact ids (#882)

* Added ArtifactId

* Added ArtifactId impl and updated into_artifacts to return id

* Formatting

* Fixed warning

* Added versioned slug method to ArtifactId

* Added Ord/Eq derives to ArtifactId

* Fixed broken tests and doc tests

* Added failing test for versioned link references

* chore: ignore linkrefs test - to be fixed in followup

* test(solc): remove artifact json

Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
Matthew Wiriyathananon-Smith 2022-02-18 22:48:56 +07:00 committed by GitHub
parent ce8c671218
commit d8e5e536cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 95 additions and 28 deletions

View File

@ -68,7 +68,7 @@ harness = false
[[test]] [[test]]
name = "project" name = "project"
path = "tests/project.rs" path = "tests/project.rs"
required-features = ["project-util"] required-features = ["async", "svm", "project-util"]
[features] [features]
default = ["rustls"] default = ["rustls"]

View File

@ -18,6 +18,36 @@ use std::{
mod configurable; mod configurable;
pub use configurable::*; pub use configurable::*;
/// Represents unique artifact metadata for identifying artifacts on output
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct ArtifactId {
/// `artifact` cache path
pub path: PathBuf,
pub name: String,
/// Original source file path
pub source: PathBuf,
/// `solc` version that produced this artifact
pub version: Version,
}
impl ArtifactId {
/// Returns a <filename>:<name> slug that identifies an artifact
pub fn slug(&self) -> String {
format!("{}.json:{}", self.path.file_stem().unwrap().to_string_lossy(), self.name)
}
/// Returns a <filename><version>:<name> slug that identifies an artifact
pub fn slug_versioned(&self) -> String {
format!(
"{}.{}.{}.{}.json:{}",
self.path.file_stem().unwrap().to_string_lossy(),
self.version.major,
self.version.minor,
self.version.patch,
self.name
)
}
}
/// Represents an artifact file representing a [`crate::Contract`] /// Represents an artifact file representing a [`crate::Contract`]
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ArtifactFile<T> { pub struct ArtifactFile<T> {
@ -162,17 +192,19 @@ impl<T> Artifacts<T> {
/// Returns an iterator over _all_ artifacts and `<file name:contract name>` /// Returns an iterator over _all_ artifacts and `<file name:contract name>`
pub fn into_artifacts<O: ArtifactOutput<Artifact = T>>( pub fn into_artifacts<O: ArtifactOutput<Artifact = T>>(
self, self,
) -> impl Iterator<Item = (String, T)> { ) -> impl Iterator<Item = (ArtifactId, T)> {
self.0.into_values().flat_map(|contract_artifacts| { self.0.into_iter().flat_map(|(file, contract_artifacts)| {
contract_artifacts.into_iter().flat_map(|(_contract_name, artifacts)| { contract_artifacts.into_iter().flat_map(move |(_contract_name, artifacts)| {
artifacts.into_iter().filter_map(|artifact| { let source = PathBuf::from(file.clone());
artifacts.into_iter().filter_map(move |artifact| {
O::contract_name(&artifact.file).map(|name| { O::contract_name(&artifact.file).map(|name| {
( (
format!( ArtifactId {
"{}:{}", path: PathBuf::from(&artifact.file),
artifact.file.file_name().unwrap().to_string_lossy(), name,
name source: source.clone(),
), version: artifact.version,
},
artifact.artifact, artifact.artifact,
) )
}) })

View File

@ -5,7 +5,7 @@ use crate::{
CompactContractBytecode, CompactContractRef, Contract, Error, SourceFile, SourceFiles, CompactContractBytecode, CompactContractRef, Contract, Error, SourceFile, SourceFiles,
}, },
contracts::{VersionedContract, VersionedContracts}, contracts::{VersionedContract, VersionedContracts},
ArtifactOutput, Artifacts, CompilerOutput, ConfigurableArtifacts, ArtifactId, ArtifactOutput, Artifacts, CompilerOutput, ConfigurableArtifacts,
}; };
use semver::Version; use semver::Version;
use std::{collections::BTreeMap, fmt, path::Path}; use std::{collections::BTreeMap, fmt, path::Path};
@ -36,12 +36,12 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
/// ```no_run /// ```no_run
/// use std::collections::btree_map::BTreeMap; /// use std::collections::btree_map::BTreeMap;
/// use ethers_solc::ConfigurableContractArtifact; /// use ethers_solc::ConfigurableContractArtifact;
/// use ethers_solc::Project; /// use ethers_solc::{ArtifactId, Project};
/// ///
/// let project = Project::builder().build().unwrap(); /// let project = Project::builder().build().unwrap();
/// let contracts: BTreeMap<String, ConfigurableContractArtifact> = project.compile().unwrap().into_artifacts().collect(); /// let contracts: BTreeMap<ArtifactId, ConfigurableContractArtifact> = project.compile().unwrap().into_artifacts().collect();
/// ``` /// ```
pub fn into_artifacts(self) -> impl Iterator<Item = (String, T::Artifact)> { pub fn into_artifacts(self) -> impl Iterator<Item = (ArtifactId, T::Artifact)> {
let Self { cached_artifacts, compiled_artifacts, .. } = self; let Self { cached_artifacts, compiled_artifacts, .. } = self;
cached_artifacts.into_artifacts::<T>().chain(compiled_artifacts.into_artifacts::<T>()) cached_artifacts.into_artifacts::<T>().chain(compiled_artifacts.into_artifacts::<T>())
} }
@ -183,15 +183,16 @@ impl ProjectCompileOutput<ConfigurableArtifacts> {
/// ```no_run /// ```no_run
/// use std::collections::btree_map::BTreeMap; /// use std::collections::btree_map::BTreeMap;
/// use ethers_solc::artifacts::CompactContractBytecode; /// use ethers_solc::artifacts::CompactContractBytecode;
/// use ethers_solc::Project; /// use ethers_solc::{ArtifactId, Project};
/// ///
/// let project = Project::builder().build().unwrap(); /// let project = Project::builder().build().unwrap();
/// let contracts: BTreeMap<String, CompactContractBytecode> = project.compile().unwrap().into_contract_bytecodes().collect(); /// let contracts: BTreeMap<ArtifactId, CompactContractBytecode> = project.compile().unwrap().into_contract_bytecodes().collect();
/// ``` /// ```
pub fn into_contract_bytecodes( pub fn into_contract_bytecodes(
self, self,
) -> impl Iterator<Item = (String, CompactContractBytecode)> { ) -> impl Iterator<Item = (ArtifactId, CompactContractBytecode)> {
self.into_artifacts().map(|(name, artifact)| (name, artifact.into_contract_bytecode())) self.into_artifacts()
.map(|(artifact_id, artifact)| (artifact_id, artifact.into_contract_bytecode()))
} }
} }

View File

@ -0,0 +1,4 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >0.4.0;
contract Lib {}

View File

@ -0,0 +1,6 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity <=0.8.10;
import "../lib/Lib.sol";
contract FooV1 {}

View File

@ -0,0 +1,6 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.11;
import "../lib/Lib.sol";
contract FooV2 {}

View File

@ -23,6 +23,24 @@ fn init_tracing() {
.init(); .init();
} }
#[test]
#[ignore]
fn can_get_versioned_linkrefs() {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/test-versioned-linkrefs");
let paths = ProjectPathsConfig::builder()
.sources(root.join("src"))
.lib(root.join("lib"))
.build()
.unwrap();
let project = Project::builder().paths(paths).ephemeral().no_artifacts().build().unwrap();
let compiled = project.compile().unwrap();
assert!(!compiled.has_compiler_errors());
// TODO:
}
#[test] #[test]
fn can_compile_hardhat_sample() { fn can_compile_hardhat_sample() {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/hardhat-sample"); let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/hardhat-sample");
@ -304,12 +322,12 @@ fn can_compile_dapp_sample_with_cache() {
assert!(compiled.find("NewContract").is_some()); assert!(compiled.find("NewContract").is_some());
assert!(!compiled.is_unchanged()); assert!(!compiled.is_unchanged());
assert_eq!( assert_eq!(
compiled.into_artifacts().map(|(name, _)| name).collect::<HashSet<_>>(), compiled.into_artifacts().map(|(artifact_id, _)| artifact_id.name).collect::<HashSet<_>>(),
HashSet::from([ HashSet::from([
"Dapp.json:Dapp".to_string(), "Dapp".to_string(),
"DappTest.json:DappTest".to_string(), "DappTest".to_string(),
"DSTest.json:DSTest".to_string(), "DSTest".to_string(),
"NewContract.json:NewContract".to_string(), "NewContract".to_string(),
]) ])
); );
@ -317,12 +335,12 @@ fn can_compile_dapp_sample_with_cache() {
std::fs::copy(cache_testdata_dir.join("Dapp.sol"), root.join("src/Dapp.sol")).unwrap(); std::fs::copy(cache_testdata_dir.join("Dapp.sol"), root.join("src/Dapp.sol")).unwrap();
let compiled = project.compile().unwrap(); let compiled = project.compile().unwrap();
assert_eq!( assert_eq!(
compiled.into_artifacts().map(|(name, _)| name).collect::<HashSet<_>>(), compiled.into_artifacts().map(|(artifact_id, _)| artifact_id.name).collect::<HashSet<_>>(),
HashSet::from([ HashSet::from([
"DappTest.json:DappTest".to_string(), "DappTest".to_string(),
"NewContract.json:NewContract".to_string(), "NewContract".to_string(),
"DSTest.json:DSTest".to_string(), "DSTest".to_string(),
"Dapp.json:Dapp".to_string(), "Dapp".to_string(),
]) ])
); );