diff --git a/ethers-solc/src/lib.rs b/ethers-solc/src/lib.rs index 4e99605a..afc6821b 100644 --- a/ethers-solc/src/lib.rs +++ b/ethers-solc/src/lib.rs @@ -235,6 +235,49 @@ impl Project { project::ProjectCompiler::with_sources(self, sources)?.compile() } + /// Convenience function to compile a single solidity file with the project's settings. + /// Same as [`Self::svm_compile()`] but with the given `file` as input. + /// + /// # Example + /// + /// ``` + /// use ethers_solc::Project; + /// # fn demo(project: Project) { + /// let project = Project::builder().build().unwrap(); + /// let output = project.compile_file("example/Greeter.sol").unwrap(); + /// # } + /// ``` + #[cfg(all(feature = "svm", feature = "async"))] + pub fn compile_file(&self, file: impl Into) -> Result> { + let file = file.into(); + let source = Source::read(&file)?; + project::ProjectCompiler::with_sources(self, Sources::from([(file, source)]))?.compile() + } + + /// Convenience function to compile a series of solidity files with the project's settings. + /// Same as [`Self::svm_compile()`] but with the given `files` as input. + /// + /// # Example + /// + /// ``` + /// use ethers_solc::Project; + /// # fn demo(project: Project) { + /// let project = Project::builder().build().unwrap(); + /// let output = project + /// .compile_files( + /// vec!["examples/Foo.sol", "examples/Bar.sol"] + /// ).unwrap(); + /// # } + /// ``` + #[cfg(all(feature = "svm", feature = "async"))] + pub fn compile_files(&self, files: I) -> Result> + where + I: IntoIterator, + P: Into, + { + project::ProjectCompiler::with_sources(self, Source::read_all(files)?)?.compile() + } + /// Compiles the given source files with the exact `Solc` executable /// /// First all libraries for the sources are resolved by scanning all their imports. diff --git a/ethers-solc/src/project_util.rs b/ethers-solc/src/project_util.rs index e5b36f5f..8683f264 100644 --- a/ethers-solc/src/project_util.rs +++ b/ethers-solc/src/project_util.rs @@ -154,12 +154,20 @@ impl TempProject { create_contract_file(lib, content) } - /// Adds a new source file + /// Adds a new source file inside the project's source dir pub fn add_source(&self, name: impl AsRef, content: impl AsRef) -> Result { let name = contract_file_name(name); let source = self.paths().sources.join(name); create_contract_file(source, content) } + + /// Adds a solidity contract in the project's root dir. + /// This will also create all intermediary dirs. + pub fn add_contract(&self, name: impl AsRef, content: impl AsRef) -> Result { + let name = contract_file_name(name); + let source = self.root().join(name); + create_contract_file(source, content) + } } impl TempProject { diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs index c1afeb4f..2a32d16f 100644 --- a/ethers-solc/tests/project.rs +++ b/ethers-solc/tests/project.rs @@ -454,3 +454,39 @@ fn can_detect_type_error() { let compiled = project.compile().unwrap(); assert!(compiled.has_compiler_errors()); } + +#[test] +fn can_compile_single_files() { + let tmp = TempProject::dapptools().unwrap(); + + let foo = tmp + .add_contract( + "examples/Foo", + r#" + pragma solidity ^0.8.10; + + contract Foo {} + "#, + ) + .unwrap(); + + let compiled = tmp.project().compile_file(foo.clone()).unwrap(); + assert!(!compiled.has_compiler_errors()); + assert!(compiled.find("Foo").is_some()); + + let bar = tmp + .add_contract( + "examples/Bar", + r#" + pragma solidity ^0.8.10; + + contract Bar {} + "#, + ) + .unwrap(); + + let compiled = tmp.project().compile_files(vec![foo, bar]).unwrap(); + assert!(!compiled.has_compiler_errors()); + assert!(compiled.find("Foo").is_some()); + assert!(compiled.find("Bar").is_some()); +}