Repository: gakonst/ethers-rs
Pull request #778 (2022-01-09 00:43:21)

refactor(solc): more temp project features 
<!--
Thank you for your Pull Request. Please provide a description above and review
the requirements below.

Bug fixes and new features should include tests.

Contributors guide: https://github.com/gakonst/ethers-rs/blob/master/CONTRIBUTING.md

The contributors guide includes instructions for running rustfmt and building the
documentation.
-->

## Motivation
* replace `tempdir` with `tempfile` crate
* more features for TempProject

Ref https://github.com/gakonst/foundry/pull/406
<!--
Explain the context and why you're making that change. What is the problem
you're trying to solve? In some cases there is not a problem and this can be
thought of as being the motivation for your change.
-->

## Solution

<!--
Summarize the solution and provide any necessary context needed to understand
the code change.
-->

## PR Checklist

- [ ] Added Tests
- [ ] Added Documentation
- [ ] Updated the changelog



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 4642c59f..ac501c20 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1352,7 +1352,7 @@ dependencies = [
  "sha2 0.9.9",
  "solang-parser",
  "svm-rs",
- "tempdir",
+ "tempfile",
  "thiserror",
  "tiny-keccak",
  "tokio",
@@ -1448,12 +1448,6 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
 
-[[package]]
-name = "fuchsia-cprng"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
-
 [[package]]
 name = "funty"
 version = "1.1.0"
@@ -2631,19 +2625,6 @@ version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb"
 
-[[package]]
-name = "rand"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
-dependencies = [
- "fuchsia-cprng",
- "libc",
- "rand_core 0.3.1",
- "rdrand",
- "winapi",
-]
-
 [[package]]
 name = "rand"
 version = "0.7.3"
@@ -2689,21 +2670,6 @@ dependencies = [
  "rand_core 0.6.3",
 ]
 
-[[package]]
-name = "rand_core"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
-dependencies = [
- "rand_core 0.4.2",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
-
 [[package]]
 name = "rand_core"
 version = "0.5.1"
@@ -2765,15 +2731,6 @@ dependencies = [
  "num_cpus",
 ]
 
-[[package]]
-name = "rdrand"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
-dependencies = [
- "rand_core 0.3.1",
-]
-
 [[package]]
 name = "redox_syscall"
 version = "0.2.10"
@@ -3534,16 +3491,6 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
 
-[[package]]
-name = "tempdir"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
-dependencies = [
- "rand 0.4.6",
- "remove_dir_all",
-]
-
 [[package]]
 name = "tempfile"
 version = "3.2.0"
diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml
index f70a4bac..7daef9b7 100644
--- a/ethers-solc/Cargo.toml
+++ b/ethers-solc/Cargo.toml
@@ -31,7 +31,7 @@ glob = "0.3.0"
 tracing = "0.1.29"
 num_cpus = "1.13.1"
 tiny-keccak = { version = "2.0.2", default-features = false }
-tempdir = { version = "0.3.7", optional = true }
+tempfile = { version = "3.2.0", optional = true }
 fs_extra = { version = "1.2.0", optional = true }
 sha2 = { version = "0.9.8", default-features = false }
 dunce = "1.0.2"
@@ -51,7 +51,7 @@ getrandom = { version = "0.2", features = ["js"] }
 [dev-dependencies]
 criterion = { version = "0.3", features = ["async_tokio"] }
 pretty_assertions = "1.0.0"
-tempdir = "0.3.7"
+tempfile = "3.2.0"
 tokio = { version = "1.15.0", features = ["full"] }
 
 [[bench]]
@@ -72,7 +72,7 @@ default = ["rustls"]
 async = ["tokio", "futures-util"]
 full = ["async", "svm"]
 # Utilities for creating and testing project workspaces
-project-util = ["tempdir", "fs_extra"]
+project-util = ["tempfile", "fs_extra"]
 tests = []
 openssl = ["svm/openssl"]
 rustls = ["svm/rustls"]
diff --git a/ethers-solc/benches/read_all.rs b/ethers-solc/benches/read_all.rs
index 6c87c620..3baccbb0 100644
--- a/ethers-solc/benches/read_all.rs
+++ b/ethers-solc/benches/read_all.rs
@@ -13,7 +13,7 @@ use std::{
 };
 
 fn read_all_benchmark(c: &mut Criterion) {
-    let root = tempdir::TempDir::new("bench_read_many").unwrap();
+    let root = tempfile::tempdir().unwrap();
     let inputs = prepare_contracts(root.path(), 8);
 
     let mut group = c.benchmark_group("read many");
diff --git a/ethers-solc/src/cache.rs b/ethers-solc/src/cache.rs
index 6dd241e6..aa6a3dd1 100644
--- a/ethers-solc/src/cache.rs
+++ b/ethers-solc/src/cache.rs
@@ -227,6 +227,12 @@ impl SolFilesCache {
     }
 }
 
+impl Default for SolFilesCache {
+    fn default() -> Self {
+        SolFilesCache { format: ETHERS_FORMAT_VERSION.to_string(), files: Default::default() }
+    }
+}
+
 #[derive(Debug, Clone, Default)]
 pub struct SolFilesCacheBuilder {
     format: Option<String>,
diff --git a/ethers-solc/src/config.rs b/ethers-solc/src/config.rs
index 50d91518..e2a69da2 100644
--- a/ethers-solc/src/config.rs
+++ b/ethers-solc/src/config.rs
@@ -591,7 +591,7 @@ mod tests {
 
     #[test]
     fn can_autodetect_dirs() {
-        let root = tempdir::TempDir::new("root").unwrap();
+        let root = crate::utils::tempdir("root").unwrap();
         let out = root.path().join("out");
         let artifacts = root.path().join("artifacts");
         let contracts = root.path().join("contracts");
@@ -633,13 +633,13 @@ mod tests {
         assert_eq!(ProjectPathsConfig::find_libs(root), vec![node_modules.clone()],);
         assert_eq!(
             ProjectPathsConfig::builder().build_with_root(&root).libraries,
-            vec![canonicalized(node_modules.clone())],
+            vec![canonicalized(node_modules)],
         );
         std::fs::File::create(&lib).unwrap();
         assert_eq!(ProjectPathsConfig::find_libs(root), vec![lib.clone()],);
         assert_eq!(
             ProjectPathsConfig::builder().build_with_root(&root).libraries,
-            vec![canonicalized(lib.clone())],
+            vec![canonicalized(lib)],
         );
     }
 }
diff --git a/ethers-solc/src/lib.rs b/ethers-solc/src/lib.rs
index 5d6c784d..058d3479 100644
--- a/ethers-solc/src/lib.rs
+++ b/ethers-solc/src/lib.rs
@@ -19,8 +19,8 @@ pub use compile::*;
 mod config;
 
 pub use config::{
-    AllowedLibPaths, Artifact, ArtifactOutput, MinimalCombinedArtifacts, ProjectPathsConfig,
-    SolcConfig,
+    AllowedLibPaths, Artifact, ArtifactOutput, MinimalCombinedArtifacts, PathStyle,
+    ProjectPathsConfig, SolcConfig,
 };
 
 pub mod remappings;
diff --git a/ethers-solc/src/project_util.rs b/ethers-solc/src/project_util.rs
index e2839fd5..34c31412 100644
--- a/ethers-solc/src/project_util.rs
+++ b/ethers-solc/src/project_util.rs
@@ -3,14 +3,18 @@ use crate::{
     config::ProjectPathsConfigBuilder,
     error::{Result, SolcError},
     hh::HardhatArtifacts,
-    ArtifactOutput, MinimalCombinedArtifacts, Project, ProjectCompileOutput, ProjectPathsConfig,
-    SolcIoError,
+    utils::tempdir,
+    ArtifactOutput, MinimalCombinedArtifacts, PathStyle, Project, ProjectCompileOutput,
+    ProjectPathsConfig, SolcIoError,
 };
 use fs_extra::{dir, file};
-use std::path::{Path, PathBuf};
-use tempdir::TempDir;
+use std::{
+    fmt,
+    path::{Path, PathBuf},
+};
+use tempfile::TempDir;
 
-pub struct TempProject<T: ArtifactOutput> {
+pub struct TempProject<T: ArtifactOutput = MinimalCombinedArtifacts> {
     /// temporary workspace root
     _root: TempDir,
     /// actual project workspace with the `root` tempdir as its root
@@ -19,19 +23,34 @@ pub struct TempProject<T: ArtifactOutput> {
 
 impl<T: ArtifactOutput> TempProject<T> {
     /// Makes sure all resources are created
-    fn create_new(root: TempDir, inner: Project<T>) -> std::result::Result<Self, SolcIoError> {
+    pub fn create_new(root: TempDir, inner: Project<T>) -> std::result::Result<Self, SolcIoError> {
         let project = Self { _root: root, inner };
         project.paths().create_all()?;
         Ok(project)
     }
 
-    pub fn new(paths: ProjectPathsConfigBuilder) -> Result<Self> {
-        let tmp_dir = TempDir::new("root").map_err(|err| SolcError::io(err, "root"))?;
+    /// Creates a new temp project inside a tempdir with a prefixed directory
+    pub fn prefixed(prefix: &str, paths: ProjectPathsConfigBuilder) -> Result<Self> {
+        let tmp_dir = tempdir(prefix)?;
         let paths = paths.build_with_root(tmp_dir.path());
         let inner = Project::builder().artifacts().paths(paths).build()?;
         Ok(Self::create_new(tmp_dir, inner)?)
     }
 
+    /// Creates a new temp project for the given `PathStyle`
+    pub fn with_style(prefix: &str, style: PathStyle) -> Result<Self> {
+        let tmp_dir = tempdir(prefix)?;
+        let paths = style.paths(tmp_dir.path())?;
+        let inner = Project::builder().artifacts().paths(paths).build()?;
+        Ok(Self::create_new(tmp_dir, inner)?)
+    }
+
+    /// Creates a new temp project using the provided paths and setting the project root to a temp
+    /// dir
+    pub fn new(paths: ProjectPathsConfigBuilder) -> Result<Self> {
+        Self::prefixed("temp-project", paths)
+    }
+
     pub fn project(&self) -> &Project<T> {
         &self.inner
     }
@@ -117,6 +136,12 @@ impl<T: ArtifactOutput> TempProject<T> {
     }
 }
 
+impl<T: ArtifactOutput> fmt::Debug for TempProject<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("TempProject").field("paths", self.paths()).finish()
+    }
+}
+
 fn create_contract_file(path: PathBuf, content: impl AsRef<str>) -> Result<PathBuf> {
     if let Some(parent) = path.parent() {
         std::fs::create_dir_all(parent)
@@ -138,7 +163,7 @@ fn contract_file_name(name: impl AsRef<str>) -> String {
 impl TempProject<HardhatArtifacts> {
     /// Creates an empty new hardhat style workspace in a new temporary dir
     pub fn hardhat() -> Result<Self> {
-        let tmp_dir = TempDir::new("tmp_hh").map_err(|err| SolcError::io(err, "tmp_hh"))?;
+        let tmp_dir = tempdir("tmp_hh")?;
 
         let paths = ProjectPathsConfig::hardhat(tmp_dir.path())?;
 
@@ -150,7 +175,7 @@ impl TempProject<HardhatArtifacts> {
 impl TempProject<MinimalCombinedArtifacts> {
     /// Creates an empty new dapptools style workspace in a new temporary dir
     pub fn dapptools() -> Result<Self> {
-        let tmp_dir = TempDir::new("tmp_dapp").map_err(|err| SolcError::io(err, "temp_dapp"))?;
+        let tmp_dir = tempdir("tmp_dapp")?;
         let paths = ProjectPathsConfig::dapptools(tmp_dir.path())?;
 
         let inner = Project::builder().artifacts().paths(paths).build()?;
diff --git a/ethers-solc/src/remappings.rs b/ethers-solc/src/remappings.rs
index ff5ebb91..7d3f4a99 100644
--- a/ethers-solc/src/remappings.rs
+++ b/ethers-solc/src/remappings.rs
@@ -356,6 +356,7 @@ fn last_nested_source_dir(root: &Path, dir: &Path) -> PathBuf {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::utils::tempdir;
 
     #[test]
     fn serde() {
@@ -398,7 +399,7 @@ mod tests {
 
     #[test]
     fn find_remapping_dapptools() {
-        let tmp_dir = tempdir::TempDir::new("lib").unwrap();
+        let tmp_dir = tempdir("lib").unwrap();
         let tmp_dir_path = tmp_dir.path();
         let paths = ["repo1/src/", "repo1/src/contract.sol"];
         mkdir_or_touch(tmp_dir_path, &paths[..]);
@@ -414,7 +415,7 @@ mod tests {
 
     #[test]
     fn recursive_remappings() {
-        let tmp_dir = tempdir::TempDir::new("lib").unwrap();
+        let tmp_dir = tempdir("lib").unwrap();
         let tmp_dir_path = tmp_dir.path();
         let paths = [
             "repo1/src/",
@@ -489,7 +490,7 @@ mod tests {
 
     #[test]
     fn remappings() {
-        let tmp_dir = tempdir::TempDir::new("tmp").unwrap();
+        let tmp_dir = tempdir("tmp").unwrap();
         let tmp_dir_path = tmp_dir.path().join("lib");
         let repo1 = tmp_dir_path.join("src_repo");
         let repo2 = tmp_dir_path.join("contracts_repo");
@@ -528,7 +529,7 @@ mod tests {
 
     #[test]
     fn simple_dapptools_remappings() {
-        let tmp_dir = tempdir::TempDir::new("lib").unwrap();
+        let tmp_dir = tempdir("lib").unwrap();
         let tmp_dir_path = tmp_dir.path();
         let paths = [
             "ds-test/src",
@@ -570,7 +571,7 @@ mod tests {
 
     #[test]
     fn hardhat_remappings() {
-        let tmp_dir = tempdir::TempDir::new("node_modules").unwrap();
+        let tmp_dir = tempdir("node_modules").unwrap();
         let tmp_dir_node_modules = tmp_dir.path().join("node_modules");
         let paths = [
             "node_modules/@aave/aave-token/contracts/token/",
diff --git a/ethers-solc/src/utils.rs b/ethers-solc/src/utils.rs
index 49a0e467..df1d1abc 100644
--- a/ethers-solc/src/utils.rs
+++ b/ethers-solc/src/utils.rs
@@ -243,6 +243,12 @@ pub(crate) fn find_fave_or_alt_path(root: impl AsRef<Path>, fave: &str, alt: &st
     p
 }
 
+/// Creates a new named tempdir
+#[cfg(any(test, feature = "project-util"))]
+pub(crate) fn tempdir(name: &str) -> Result<tempfile::TempDir, SolcIoError> {
+    tempfile::Builder::new().prefix(name).tempdir().map_err(|err| SolcIoError::new(err, name))
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -251,7 +257,7 @@ mod tests {
         fs::{create_dir_all, File},
     };
 
-    use tempdir::TempDir;
+    use tempdir;
 
     #[test]
     fn can_determine_local_paths() {
@@ -259,7 +265,7 @@ mod tests {
         assert!(is_local_source_name(&[""], "../local/contract.sol"));
         assert!(!is_local_source_name(&[""], "/ds-test/test.sol"));
 
-        let tmp_dir = TempDir::new("contracts").unwrap();
+        let tmp_dir = tempdir("contracts").unwrap();
         let dir = tmp_dir.path().join("ds-test");
         create_dir_all(&dir).unwrap();
         File::create(dir.join("test.sol")).unwrap();
@@ -269,7 +275,7 @@ mod tests {
 
     #[test]
     fn can_find_solidity_sources() {
-        let tmp_dir = TempDir::new("contracts").unwrap();
+        let tmp_dir = tempdir("contracts").unwrap();
 
         let file_a = tmp_dir.path().join("a.sol");
         let file_b = tmp_dir.path().join("a.sol");


Test patch:
diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs
index 86c2d290..f178dfff 100644
--- a/ethers-solc/tests/project.rs
+++ b/ethers-solc/tests/project.rs
@@ -7,8 +7,6 @@ use std::{
     str::FromStr,
 };
 
-use tempdir::TempDir;
-
 use ethers_solc::{
     cache::{SolFilesCache, SOLIDITY_FILES_CACHE_FILENAME},
     project_util::*,
@@ -141,7 +139,7 @@ fn can_compile_dapp_detect_changes_in_libs() {
 
 #[test]
 fn can_compile_dapp_sample_with_cache() {
-    let tmp_dir = TempDir::new("root").unwrap();
+    let tmp_dir = tempfile::tempdir().unwrap();
     let root = tmp_dir.path();
     let cache = root.join("cache").join(SOLIDITY_FILES_CACHE_FILENAME);
     let artifacts = root.join("out");