foundry_compilers/compile/output/
info.rs1use std::{borrow::Cow, fmt, path::Path, str::FromStr};
4
5#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
6#[error("{0}")]
7pub struct ParseContractInfoError(String);
8
9#[derive(Clone, Debug, PartialEq, Eq, Hash)]
12pub struct ContractInfo {
13 pub path: Option<String>,
15 pub name: String,
17}
18
19impl ContractInfo {
22 pub fn new(info: &str) -> Self {
41 info.parse().unwrap_or_else(|_| Self { path: None, name: info.to_string() })
42 }
43
44 pub fn path(&self) -> Option<&Path> {
46 self.path.as_deref().map(Path::new)
47 }
48}
49
50impl fmt::Display for ContractInfo {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 if let Some(path) = &self.path {
53 write!(f, "{path}:")?;
54 }
55 f.write_str(&self.name)
56 }
57}
58
59impl FromStr for ContractInfo {
60 type Err = ParseContractInfoError;
61
62 fn from_str(s: &str) -> Result<Self, Self::Err> {
63 let err = || {
64 ParseContractInfoError(
65 "contract source info format must be `<path>:<contractname>` or `<contractname>`"
66 .to_string(),
67 )
68 };
69 let mut iter = s.rsplit(':');
70 let name = iter.next().ok_or_else(err)?.trim().to_string();
71 let path = iter.next().map(str::to_string);
72
73 if name.ends_with(".sol") || name.contains('/') {
74 return Err(err());
75 }
76
77 Ok(Self { path, name })
78 }
79}
80
81impl From<FullContractInfo> for ContractInfo {
82 fn from(info: FullContractInfo) -> Self {
83 let FullContractInfo { path, name } = info;
84 Self { path: Some(path), name }
85 }
86}
87
88#[derive(Clone, Debug, PartialEq, Eq, Hash)]
90pub struct ContractInfoRef<'a> {
91 pub path: Option<Cow<'a, str>>,
92 pub name: Cow<'a, str>,
93}
94
95impl From<ContractInfo> for ContractInfoRef<'_> {
96 fn from(info: ContractInfo) -> Self {
97 ContractInfoRef { path: info.path.map(Into::into), name: info.name.into() }
98 }
99}
100
101impl<'a> From<&'a ContractInfo> for ContractInfoRef<'a> {
102 fn from(info: &'a ContractInfo) -> Self {
103 ContractInfoRef {
104 path: info.path.as_deref().map(Into::into),
105 name: info.name.as_str().into(),
106 }
107 }
108}
109impl From<FullContractInfo> for ContractInfoRef<'_> {
110 fn from(info: FullContractInfo) -> Self {
111 ContractInfoRef { path: Some(info.path.into()), name: info.name.into() }
112 }
113}
114
115impl<'a> From<&'a FullContractInfo> for ContractInfoRef<'a> {
116 fn from(info: &'a FullContractInfo) -> Self {
117 ContractInfoRef { path: Some(info.path.as_str().into()), name: info.name.as_str().into() }
118 }
119}
120
121#[derive(Clone, Debug, PartialEq, Eq, Hash)]
123pub struct FullContractInfo {
124 pub path: String,
126 pub name: String,
128}
129
130impl fmt::Display for FullContractInfo {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 write!(f, "{}:{}", self.path, self.name)
133 }
134}
135
136impl FromStr for FullContractInfo {
137 type Err = ParseContractInfoError;
138
139 fn from_str(s: &str) -> Result<Self, Self::Err> {
140 let (path, name) = s.split_once(':').ok_or_else(|| {
141 ParseContractInfoError("Expected `<path>:<contractname>`, got `{s}`".to_string())
142 })?;
143 Ok(Self { path: path.to_string(), name: name.trim().to_string() })
144 }
145}
146
147impl TryFrom<ContractInfo> for FullContractInfo {
148 type Error = ParseContractInfoError;
149
150 fn try_from(value: ContractInfo) -> Result<Self, Self::Error> {
151 let ContractInfo { path, name } = value;
152 Ok(Self {
153 path: path.ok_or_else(|| {
154 ParseContractInfoError("path to contract must be present".to_string())
155 })?,
156 name,
157 })
158 }
159}