Skip to content

Commit

Permalink
feat: additional helpers for contract name -> path lookup (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
klkvr authored Apr 17, 2024
1 parent d0fb2f5 commit d4c5ee7
Showing 1 changed file with 85 additions and 2 deletions.
87 changes: 85 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub mod remappings;

mod filter;
pub use filter::{FileFilter, TestFileFilter};
use solang_parser::pt::SourceUnitPart;

pub mod report;

Expand All @@ -55,12 +56,13 @@ use crate::{
error::{SolcError, SolcIoError},
sources::{VersionedSourceFile, VersionedSourceFiles},
};
use artifacts::{contract::Contract, Severity};
use artifacts::{contract::Contract, output_selection::OutputSelection, Severity};
use compile::output::contracts::VersionedContracts;
use error::Result;
use semver::Version;
use std::{
collections::{BTreeMap, HashSet},
collections::{BTreeMap, HashMap, HashSet},
fs,
path::{Path, PathBuf},
};

Expand Down Expand Up @@ -559,6 +561,87 @@ impl<T: ArtifactOutput> Project<T> {

Ok(input)
}

/// Runs solc compiler without requesting any output and collects a mapping from contract names
/// to source files containing artifact with given name.
fn collect_contract_names_solc(&self) -> Result<HashMap<String, Vec<PathBuf>>>
where
T: Clone,
{
let mut temp_project = (*self).clone();
temp_project.no_artifacts = true;
temp_project.solc_config.settings.output_selection =
OutputSelection::common_output_selection([]);

let output = temp_project.compile()?;

if output.has_compiler_errors() {
return Err(SolcError::msg(output));
}

let contracts = output.into_artifacts().fold(
HashMap::new(),
|mut contracts: HashMap<_, Vec<_>>, (id, _)| {
contracts.entry(id.name).or_default().push(id.source);
contracts
},
);

Ok(contracts)
}

/// Parses project sources via solang parser, collecting mapping from contract name to source
/// files containing artifact with given name. On parser failure, fallbacks to
/// [Self::collect_contract_names_solc].
fn collect_contract_names(&self) -> Result<HashMap<String, Vec<PathBuf>>>
where
T: Clone,
{
let graph = Graph::resolve(&self.paths)?;
let mut contracts: HashMap<String, Vec<PathBuf>> = HashMap::new();

for file in graph.files().keys() {
let src = fs::read_to_string(file).map_err(|e| SolcError::io(e, file))?;
let Ok((parsed, _)) = solang_parser::parse(&src, 0) else {
return self.collect_contract_names_solc();
};

for part in parsed.0 {
if let SourceUnitPart::ContractDefinition(contract) = part {
if let Some(name) = contract.name {
contracts.entry(name.name).or_default().push(file.clone());
}
}
}
}

Ok(contracts)
}

/// Finds the path of the contract with the given name.
/// Throws error if multiple or no contracts with the same name are found.
pub fn find_contract_path(&self, target_name: &str) -> Result<PathBuf>
where
T: Clone,
{
let mut contracts = self.collect_contract_names()?;

if contracts.get(target_name).map_or(true, |paths| paths.is_empty()) {
return Err(SolcError::msg(format!(
"No contract found with the name `{}`",
target_name
)));
}
let mut paths = contracts.remove(target_name).unwrap();
if paths.len() > 1 {
return Err(SolcError::msg(format!(
"Multiple contracts found with the name `{}`",
target_name
)));
}

Ok(paths.remove(0))
}
}

pub struct ProjectBuilder<T: ArtifactOutput = ConfigurableArtifacts> {
Expand Down

0 comments on commit d4c5ee7

Please sign in to comment.