use std::{fs, path::Path};
use syn::{Attribute, Item, ItemImpl};
use tracing::{error, warn};
use crate::primitivies::{ContractError, ContractWithDeps};
pub fn obtain_contract_by_path(path: &Path) -> Option<ContractWithDeps> {
if !path.is_dir() {
return None;
}
let cargo_path = path.join("Cargo.toml");
if !cargo_path.exists() {
return None;
}
match ContractWithDeps::try_from(&cargo_path) {
Ok(mut contract) => {
let lib_path = contract.path.join("src").join("lib.rs");
let ident = match find_contract_ident(&lib_path) {
Ok(ident) => ident,
Err(e) => {
error!(
"Unable to find contract identifier at {:?}: {:?}",
lib_path, e
);
return None;
}
};
contract.name.ident = ident;
for _dep in &mut contract.deps {
}
return Some(contract);
}
Err(ContractError::MissingDependencies) => None,
Err(ContractError::MissingBinaries) => None,
Err(ContractError::MissingFeatures) => None,
Err(e) => {
warn!(
"Error parsing potential contract at {:?}: {:?}",
cargo_path, e
);
None
}
}
}
pub fn find_contract_ident(file_path: &Path) -> Result<String, anyhow::Error> {
let content = fs::read_to_string(file_path)?;
let file = syn::parse_file(&content)?;
for item in file.items {
if let Item::Impl(item_impl) = item {
if has_contract_attribute(&item_impl.attrs) {
if let Some(ident) = extract_ident(&item_impl) {
return Ok(ident);
}
}
}
}
anyhow::bail!("No contract implementation found in file: {:?}", file_path)
}
fn has_contract_attribute(attrs: &[Attribute]) -> bool {
attrs
.iter()
.any(|attr| attr.path.segments.len() == 1 && attr.path.segments[0].ident == "contract")
}
fn extract_ident(item_impl: &ItemImpl) -> Option<String> {
match &*item_impl.self_ty {
syn::Type::Path(type_path) if !type_path.path.segments.is_empty() => {
let segment = type_path.path.segments.last().unwrap();
Some(segment.ident.to_string())
}
_ => None,
}
}