ethers_solc/compile/output/
info.rs

1//! Commonly used identifiers for contracts in the compiled output
2use std::{borrow::Cow, fmt, str::FromStr};
3
4#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
5#[error("{0}")]
6pub struct ParseContractInfoError(String);
7
8/// Represents the common contract argument pattern for `<path>:<contractname>` where `<path>:` is
9/// optional.
10#[derive(Clone, Debug, Eq, PartialEq, Hash)]
11pub struct ContractInfo {
12    /// Location of the contract
13    pub path: Option<String>,
14    /// Name of the contract
15    pub name: String,
16}
17
18// === impl ContractInfo ===
19
20impl ContractInfo {
21    /// Creates a new `ContractInfo` from the `info` str.
22    ///
23    /// This will attempt `ContractInfo::from_str`, if `info` matches the `<path>:<name>` format,
24    /// the `ContractInfo`'s `path` will be set.
25    ///
26    /// otherwise the `name` of the new object will be `info`.
27    ///
28    /// # Example
29    ///
30    /// ```
31    ///  use ethers_solc::info::ContractInfo;
32    /// let info = ContractInfo::new("src/Greeter.sol:Greeter");
33    /// assert_eq!(info, ContractInfo {path: Some("src/Greeter.sol".to_string()), name: "Greeter".to_string()});
34    /// ```
35    pub fn new(info: impl AsRef<str>) -> Self {
36        let info = info.as_ref();
37        info.parse().unwrap_or_else(|_| ContractInfo { path: None, name: info.to_string() })
38    }
39}
40
41impl fmt::Display for ContractInfo {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        if let Some(ref path) = self.path {
44            write!(f, "{path}:{}", self.name)
45        } else {
46            write!(f, "{}", self.name)
47        }
48    }
49}
50
51impl FromStr for ContractInfo {
52    type Err = ParseContractInfoError;
53
54    fn from_str(s: &str) -> Result<Self, Self::Err> {
55        let err = || {
56            ParseContractInfoError(
57                "contract source info format must be `<path>:<contractname>` or `<contractname>`"
58                    .to_string(),
59            )
60        };
61        let mut iter = s.rsplit(':');
62        let name = iter.next().ok_or_else(err)?.trim().to_string();
63        let path = iter.next().map(str::to_string);
64
65        if name.ends_with(".sol") || name.contains('/') {
66            return Err(err())
67        }
68
69        Ok(Self { path, name })
70    }
71}
72
73impl From<FullContractInfo> for ContractInfo {
74    fn from(info: FullContractInfo) -> Self {
75        let FullContractInfo { path, name } = info;
76        ContractInfo { path: Some(path), name }
77    }
78}
79
80/// The reference type for `ContractInfo`
81#[derive(Clone, Debug, Eq, PartialEq, Hash)]
82pub struct ContractInfoRef<'a> {
83    pub path: Option<Cow<'a, str>>,
84    pub name: Cow<'a, str>,
85}
86
87impl<'a> From<ContractInfo> for ContractInfoRef<'a> {
88    fn from(info: ContractInfo) -> Self {
89        ContractInfoRef { path: info.path.map(Into::into), name: info.name.into() }
90    }
91}
92
93impl<'a> From<&'a ContractInfo> for ContractInfoRef<'a> {
94    fn from(info: &'a ContractInfo) -> Self {
95        ContractInfoRef {
96            path: info.path.as_deref().map(Into::into),
97            name: info.name.as_str().into(),
98        }
99    }
100}
101impl<'a> From<FullContractInfo> for ContractInfoRef<'a> {
102    fn from(info: FullContractInfo) -> Self {
103        ContractInfoRef { path: Some(info.path.into()), name: info.name.into() }
104    }
105}
106
107impl<'a> From<&'a FullContractInfo> for ContractInfoRef<'a> {
108    fn from(info: &'a FullContractInfo) -> Self {
109        ContractInfoRef { path: Some(info.path.as_str().into()), name: info.name.as_str().into() }
110    }
111}
112
113/// Represents the common contract argument pattern `<path>:<contractname>`
114#[derive(Clone, Debug, Eq, PartialEq, Hash)]
115pub struct FullContractInfo {
116    /// Location of the contract
117    pub path: String,
118    /// Name of the contract
119    pub name: String,
120}
121
122impl fmt::Display for FullContractInfo {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        write!(f, "{}:{}", self.path, self.name)
125    }
126}
127
128impl FromStr for FullContractInfo {
129    type Err = ParseContractInfoError;
130
131    fn from_str(s: &str) -> Result<Self, Self::Err> {
132        let (path, name) = s.split_once(':').ok_or_else(|| {
133            ParseContractInfoError("Expected `<path>:<contractname>`, got `{s}`".to_string())
134        })?;
135        Ok(Self { path: path.to_string(), name: name.trim().to_string() })
136    }
137}
138
139impl TryFrom<ContractInfo> for FullContractInfo {
140    type Error = ParseContractInfoError;
141
142    fn try_from(value: ContractInfo) -> Result<Self, Self::Error> {
143        let ContractInfo { path, name } = value;
144        Ok(FullContractInfo {
145            path: path.ok_or_else(|| {
146                ParseContractInfoError("path to contract must be present".to_string())
147            })?,
148            name,
149        })
150    }
151}