diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml index 3530f53c..4a7cf974 100644 --- a/ethers-solc/Cargo.toml +++ b/ethers-solc/Cargo.toml @@ -18,7 +18,7 @@ keywords = ["ethereum", "web3", "solc", "solidity", "ethers"] [dependencies] ethers-core = { version = "^1.0.0", path = "../ethers-core", default-features = false } serde_json = "1.0.68" -serde = { version = "1.0.130", features = ["derive"] } +serde = { version = "1.0.130", features = ["derive", "rc"] } semver = { version = "1.0.16", features = ["serde"] } walkdir = "2.3.2" tokio = { version = "1.18", default-features = false, features = ["rt"] } diff --git a/ethers-solc/src/artifacts/mod.rs b/ethers-solc/src/artifacts/mod.rs index a90b0962..35bedb92 100644 --- a/ethers-solc/src/artifacts/mod.rs +++ b/ethers-solc/src/artifacts/mod.rs @@ -11,6 +11,7 @@ use std::{ fmt, fs, path::{Path, PathBuf}, str::FromStr, + sync::Arc, }; use tracing::warn; use yansi::Paint; @@ -1195,16 +1196,29 @@ pub struct DocLibraries { pub libs: BTreeMap, } +/// Content of a solidity file +/// +/// This contains the actual source code of a file #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] pub struct Source { - pub content: String, + /// Content of the file + /// + /// This is an `Arc` because it may be cloned. If the [Graph](crate::resolver::Graph) of the + /// project contains multiple conflicting versions then the same [Source] may be required by + /// conflicting versions and needs to be duplicated. + pub content: Arc, } impl Source { - /// Reads the file content + /// Creates a new instance of [Source] with the given content. + pub fn new(content: impl Into) -> Self { + Self { content: Arc::new(content.into()) } + } + + /// Reads the file's content pub fn read(file: impl AsRef) -> Result { let file = file.as_ref(); - Ok(Self { content: fs::read_to_string(file).map_err(|err| SolcIoError::new(err, file))? }) + Ok(Self::new(fs::read_to_string(file).map_err(|err| SolcIoError::new(err, file))?)) } /// Recursively finds all source files under the given dir path and reads them all @@ -1254,7 +1268,7 @@ impl Source { /// Generate a non-cryptographically secure checksum of the file's content pub fn content_hash(&self) -> String { let mut hasher = md5::Md5::new(); - hasher.update(&self.content); + hasher.update(self); let result = hasher.finalize(); hex::encode(result) } @@ -1270,11 +1284,9 @@ impl Source { /// async version of `Self::read` pub async fn async_read(file: impl AsRef) -> Result { let file = file.as_ref(); - Ok(Self { - content: tokio::fs::read_to_string(file) - .await - .map_err(|err| SolcIoError::new(err, file))?, - }) + Ok(Self::new( + tokio::fs::read_to_string(file).await.map_err(|err| SolcIoError::new(err, file))?, + )) } /// Finds all source files under the given dir path and reads them all @@ -1306,6 +1318,12 @@ impl AsRef for Source { } } +impl AsRef<[u8]> for Source { + fn as_ref(&self) -> &[u8] { + self.content.as_bytes() + } +} + /// Output type `solc` produces #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)] pub struct CompilerOutput { diff --git a/ethers-solc/src/buildinfo.rs b/ethers-solc/src/buildinfo.rs index 97507872..27eaffa0 100644 --- a/ethers-solc/src/buildinfo.rs +++ b/ethers-solc/src/buildinfo.rs @@ -104,7 +104,7 @@ mod tests { fn build_info_serde() { let inputs = CompilerInput::with_sources(BTreeMap::from([( PathBuf::from("input.sol"), - Source { content: "".to_string() }, + Source::new(""), )])); let output = CompilerOutput::default(); let v: Version = "0.8.4+commit.c7e474f2".parse().unwrap(); diff --git a/ethers-solc/src/compile/mod.rs b/ethers-solc/src/compile/mod.rs index a00123c9..25d5307f 100644 --- a/ethers-solc/src/compile/mod.rs +++ b/ethers-solc/src/compile/mod.rs @@ -909,6 +909,6 @@ mod tests { ///// helpers fn source(version: &str) -> Source { - Source { content: format!("pragma solidity {version};\n") } + Source::new(format!("pragma solidity {version};\n")) } }