diff --git a/ethers-solc/src/config.rs b/ethers-solc/src/config.rs index 28d4b00f..cbb1aea3 100644 --- a/ethers-solc/src/config.rs +++ b/ethers-solc/src/config.rs @@ -4,7 +4,7 @@ use crate::{ error::{Result, SolcError, SolcIoError}, hh::HardhatArtifact, remappings::Remapping, - CompilerOutput, + utils, CompilerOutput, }; use ethers_core::{abi::Abi, types::Bytes}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -75,6 +75,36 @@ impl ProjectPathsConfig { } Ok(()) } + + /// Attempts to autodetect the artifacts directory based on the given root path + /// + /// Dapptools layout takes precedence over hardhat style. + /// This will return: + /// - `/out` if it exists or `/artifacts` does not exist, + /// - `/artifacts` if it exists and `/out` does not exist. + pub fn find_artifacts_dir(root: impl AsRef) -> PathBuf { + utils::find_fave_or_alt_path(root, "out", "artifacts") + } + + /// Attempts to autodetect the source directory based on the given root path + /// + /// Dapptools layout takes precedence over hardhat style. + /// This will return: + /// - `/src` if it exists or `/contracts` does not exist, + /// - `/contracts` if it exists and `/src` does not exist. + pub fn find_source_dir(root: impl AsRef) -> PathBuf { + utils::find_fave_or_alt_path(root, "src", "contracts") + } + + /// Attempts to autodetect the lib directory based on the given root path + /// + /// Dapptools layout takes precedence over hardhat style. + /// This will return: + /// - `/lib` if it exists or `/node_modules` does not exist, + /// - `/node_modules` if it exists and `/lib` does not exist. + pub fn find_libs(root: impl AsRef) -> Vec { + vec![utils::find_fave_or_alt_path(root, "lib", "node_modules")] + } } impl fmt::Display for ProjectPathsConfig { @@ -195,14 +225,17 @@ impl ProjectPathsConfigBuilder { pub fn build_with_root(self, root: impl Into) -> ProjectPathsConfig { let root = root.into(); + ProjectPathsConfig { cache: self .cache .unwrap_or_else(|| root.join("cache").join(SOLIDITY_FILES_CACHE_FILENAME)), - artifacts: self.artifacts.unwrap_or_else(|| root.join("artifacts")), - sources: self.sources.unwrap_or_else(|| root.join("contracts")), + artifacts: self + .artifacts + .unwrap_or_else(|| ProjectPathsConfig::find_artifacts_dir(&root)), + sources: self.sources.unwrap_or_else(|| ProjectPathsConfig::find_source_dir(&root)), tests: self.tests.unwrap_or_else(|| root.join("tests")), - libraries: self.libraries.unwrap_or_default(), + libraries: self.libraries.unwrap_or_else(|| ProjectPathsConfig::find_libs(&root)), remappings: self.remappings.unwrap_or_default(), root, } @@ -491,3 +524,56 @@ impl> TryFrom> for AllowedLibPaths { Ok(AllowedLibPaths(libs)) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_autodetect_dirs() { + let root = tempdir::TempDir::new("root").unwrap(); + let out = root.path().join("out"); + let artifacts = root.path().join("artifacts"); + let contracts = root.path().join("contracts"); + let src = root.path().join("src"); + let lib = root.path().join("lib"); + let node_modules = root.path().join("node_modules"); + + let root = root.path(); + assert_eq!(ProjectPathsConfig::find_source_dir(root), src,); + assert_eq!(ProjectPathsConfig::builder().build_with_root(&root).sources, src,); + std::fs::File::create(&contracts).unwrap(); + assert_eq!(ProjectPathsConfig::find_source_dir(root), contracts,); + assert_eq!(ProjectPathsConfig::builder().build_with_root(&root).sources, contracts,); + std::fs::File::create(&src).unwrap(); + assert_eq!(ProjectPathsConfig::find_source_dir(root), src,); + assert_eq!(ProjectPathsConfig::builder().build_with_root(&root).sources, src,); + + assert_eq!(ProjectPathsConfig::find_artifacts_dir(root), out,); + assert_eq!(ProjectPathsConfig::builder().build_with_root(&root).artifacts, out,); + std::fs::File::create(&artifacts).unwrap(); + assert_eq!(ProjectPathsConfig::find_artifacts_dir(root), artifacts,); + assert_eq!(ProjectPathsConfig::builder().build_with_root(&root).artifacts, artifacts,); + std::fs::File::create(&out).unwrap(); + assert_eq!(ProjectPathsConfig::find_artifacts_dir(root), out,); + assert_eq!(ProjectPathsConfig::builder().build_with_root(&root).artifacts, out,); + + assert_eq!(ProjectPathsConfig::find_libs(root), vec![lib.clone()],); + assert_eq!( + ProjectPathsConfig::builder().build_with_root(&root).libraries, + vec![lib.clone()], + ); + std::fs::File::create(&node_modules).unwrap(); + assert_eq!(ProjectPathsConfig::find_libs(root), vec![node_modules.clone()],); + assert_eq!( + ProjectPathsConfig::builder().build_with_root(&root).libraries, + vec![node_modules.clone()], + ); + 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![lib.clone()], + ); + } +} diff --git a/ethers-solc/src/utils.rs b/ethers-solc/src/utils.rs index 269d43a5..6fad33ed 100644 --- a/ethers-solc/src/utils.rs +++ b/ethers-solc/src/utils.rs @@ -220,6 +220,22 @@ pub fn common_ancestor(a: impl AsRef, b: impl AsRef) -> Option/` if it exists or `/` does not exist, +/// Returns `/` if it exists and `/` does not exist. +pub(crate) fn find_fave_or_alt_path(root: impl AsRef, fave: &str, alt: &str) -> PathBuf { + let root = root.as_ref(); + let p = root.join(fave); + if !p.exists() { + let alt = root.join(alt); + if alt.exists() { + return alt + } + } + p +} + #[cfg(test)] mod tests { use super::*;