//! Commonly used identifiers for contracts in the compiled output use std::{convert::TryFrom, fmt, str::FromStr}; #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] #[error("{0}")] pub struct ParseContractInfoError(String); /// Represents the common contract argument pattern for `:` where `:` is /// optional. #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct ContractInfo { /// Location of the contract pub path: Option, /// Name of the contract pub name: String, } // === impl ContractInfo === impl ContractInfo { /// Creates a new `ContractInfo` from the `info` str. /// /// This will attempt `ContractInfo::from_str`, if `info` matches the `:` format, /// the `ContractInfo`'s `path` will be set. /// /// otherwise the `name` of the new object will be `info`. /// /// # Example /// /// ``` /// use ethers_solc::info::ContractInfo; /// let info = ContractInfo::new("src/Greeter.sol:Greeter"); /// assert_eq!(info, ContractInfo {path: Some("src/Greeter.sol".to_string()), name: "Greeter".to_string()}); /// ``` pub fn new(info: impl AsRef) -> Self { let info = info.as_ref(); info.parse().unwrap_or_else(|_| ContractInfo { path: None, name: info.to_string() }) } } impl fmt::Display for ContractInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(ref path) = self.path { write!(f, "{}:{}", path, self.name) } else { write!(f, "{}", self.name) } } } impl FromStr for ContractInfo { type Err = ParseContractInfoError; fn from_str(s: &str) -> Result { let err = || { ParseContractInfoError( "contract source info format must be `:` or ``" .to_string(), ) }; let mut iter = s.rsplit(':'); let name = iter.next().ok_or_else(err)?.trim().to_string(); let path = iter.next().map(str::to_string); if name.ends_with(".sol") || name.contains('/') { return Err(err()) } Ok(Self { path, name }) } } impl From for ContractInfo { fn from(info: FullContractInfo) -> Self { let FullContractInfo { path, name } = info; ContractInfo { path: Some(path), name } } } /// Represents the common contract argument pattern `:` #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct FullContractInfo { /// Location of the contract pub path: String, /// Name of the contract pub name: String, } impl fmt::Display for FullContractInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", self.path, self.name) } } impl FromStr for FullContractInfo { type Err = ParseContractInfoError; fn from_str(s: &str) -> Result { let (path, name) = s.split_once(':').ok_or_else(|| { ParseContractInfoError("Expected `:`, got `{s}`".to_string()) })?; Ok(Self { path: path.to_string(), name: name.trim().to_string() }) } } impl TryFrom for FullContractInfo { type Error = ParseContractInfoError; fn try_from(value: ContractInfo) -> Result { let ContractInfo { path, name } = value; Ok(FullContractInfo { path: path.ok_or_else(|| { ParseContractInfoError("path to contract must be present".to_string()) })?, name, }) } }