From 3d77f44066d49c12f8999fbd4d1ebd5e45e25a17 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 23 Jun 2022 04:37:39 +0200 Subject: [PATCH] feat(solc): add Contract Info structs (#1407) --- ethers-solc/src/compile/mod.rs | 2 +- ethers-solc/src/compile/output/info.rs | 118 +++++++++++++++++++++++++ ethers-solc/src/compile/output/mod.rs | 1 + 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 ethers-solc/src/compile/output/info.rs diff --git a/ethers-solc/src/compile/mod.rs b/ethers-solc/src/compile/mod.rs index b57b1c37..e1b6217a 100644 --- a/ethers-solc/src/compile/mod.rs +++ b/ethers-solc/src/compile/mod.rs @@ -14,7 +14,7 @@ use std::{ }; pub mod many; pub mod output; -pub use output::{contracts, sources}; +pub use output::{contracts, info, sources}; pub mod project; /// The name of the `solc` binary on the system diff --git a/ethers-solc/src/compile/output/info.rs b/ethers-solc/src/compile/output/info.rs new file mode 100644 index 00000000..8e118cea --- /dev/null +++ b/ethers-solc/src/compile/output/info.rs @@ -0,0 +1,118 @@ +//! 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, + }) + } +} diff --git a/ethers-solc/src/compile/output/mod.rs b/ethers-solc/src/compile/output/mod.rs index 0d0ef629..c7e10779 100644 --- a/ethers-solc/src/compile/output/mod.rs +++ b/ethers-solc/src/compile/output/mod.rs @@ -15,6 +15,7 @@ use std::{collections::BTreeMap, fmt, path::Path}; use tracing::trace; pub mod contracts; +pub mod info; pub mod sources; /// Contains a mixture of already compiled/cached artifacts and the input set of sources that still