1use alloy_primitives::map::HashSet;
3use figment::providers::{Format, Toml};
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::{error::Error, fmt, str::FromStr};
6
7#[derive(Clone, PartialEq)]
9pub struct ExtractConfigError {
10 pub(crate) error: figment::Error,
12}
13
14impl ExtractConfigError {
15 pub fn new(error: figment::Error) -> Self {
17 Self { error }
18 }
19}
20
21impl fmt::Display for ExtractConfigError {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 let mut unique_errors = Vec::with_capacity(self.error.count());
24 let mut unique = HashSet::with_capacity(self.error.count());
25 for err in self.error.clone().into_iter() {
26 let err = if err
27 .metadata
28 .as_ref()
29 .map(|meta| meta.name.contains(Toml::NAME))
30 .unwrap_or_default()
31 {
32 FoundryConfigError::Toml(err)
33 } else {
34 FoundryConfigError::Other(err)
35 };
36
37 if unique.insert(err.to_string()) {
38 unique_errors.push(err);
39 }
40 }
41 writeln!(f, "failed to extract foundry config:")?;
42 for err in unique_errors {
43 writeln!(f, "{err}")?;
44 }
45 Ok(())
46 }
47}
48
49impl fmt::Debug for ExtractConfigError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 fmt::Display::fmt(self, f)
52 }
53}
54
55impl Error for ExtractConfigError {
56 fn source(&self) -> Option<&(dyn Error + 'static)> {
57 Error::source(&self.error)
58 }
59}
60
61#[derive(Clone, Debug, PartialEq)]
63pub enum FoundryConfigError {
64 Toml(figment::Error),
66 Other(figment::Error),
68}
69
70impl fmt::Display for FoundryConfigError {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 let fmt_err = |err: &figment::Error, f: &mut fmt::Formatter<'_>| {
73 write!(f, "{err}")?;
74 if !err.path.is_empty() {
75 write!(f, " for setting `{}`", err.path.join("."))?;
77 }
78 Ok(())
79 };
80
81 match self {
82 Self::Toml(err) => {
83 f.write_str("foundry.toml error: ")?;
84 fmt_err(err, f)
85 }
86 Self::Other(err) => {
87 f.write_str("foundry config error: ")?;
88 fmt_err(err, f)
89 }
90 }
91 }
92}
93
94impl Error for FoundryConfigError {
95 fn source(&self) -> Option<&(dyn Error + 'static)> {
96 match self {
97 Self::Other(error) | Self::Toml(error) => Error::source(error),
98 }
99 }
100}
101
102#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104pub enum SolidityErrorCode {
105 SpdxLicenseNotProvided,
107 VisibilityForConstructorIsIgnored,
110 ContractExceeds24576Bytes,
113 ContractInitCodeSizeExceeds49152Bytes,
115 FunctionStateMutabilityCanBeRestricted,
117 UnusedLocalVariable,
119 UnusedFunctionParameter,
122 ReturnValueOfCallsNotUsed,
124 InterfacesExplicitlyVirtual,
126 PayableNoReceiveEther,
129 ShadowsExistingDeclaration,
131 DeclarationSameNameAsAnother,
133 UnnamedReturnVariable,
135 Unreachable,
137 PragmaSolidity,
139 TransientStorageUsed,
141 TooManyWarnings,
143 Other(u64),
145}
146
147impl SolidityErrorCode {
148 pub fn as_str(&self) -> Result<&'static str, u64> {
152 let s = match self {
153 Self::SpdxLicenseNotProvided => "license",
154 Self::VisibilityForConstructorIsIgnored => "constructor-visibility",
155 Self::ContractExceeds24576Bytes => "code-size",
156 Self::ContractInitCodeSizeExceeds49152Bytes => "init-code-size",
157 Self::FunctionStateMutabilityCanBeRestricted => "func-mutability",
158 Self::UnusedLocalVariable => "unused-var",
159 Self::UnusedFunctionParameter => "unused-param",
160 Self::ReturnValueOfCallsNotUsed => "unused-return",
161 Self::InterfacesExplicitlyVirtual => "virtual-interfaces",
162 Self::PayableNoReceiveEther => "missing-receive-ether",
163 Self::ShadowsExistingDeclaration => "shadowing",
164 Self::DeclarationSameNameAsAnother => "same-varname",
165 Self::UnnamedReturnVariable => "unnamed-return",
166 Self::Unreachable => "unreachable",
167 Self::PragmaSolidity => "pragma-solidity",
168 Self::TransientStorageUsed => "transient-storage",
169 Self::TooManyWarnings => "too-many-warnings",
170 Self::Other(code) => return Err(*code),
171 };
172 Ok(s)
173 }
174}
175
176impl From<SolidityErrorCode> for u64 {
177 fn from(code: SolidityErrorCode) -> Self {
178 match code {
179 SolidityErrorCode::SpdxLicenseNotProvided => 1878,
180 SolidityErrorCode::VisibilityForConstructorIsIgnored => 2462,
181 SolidityErrorCode::ContractExceeds24576Bytes => 5574,
182 SolidityErrorCode::ContractInitCodeSizeExceeds49152Bytes => 3860,
183 SolidityErrorCode::FunctionStateMutabilityCanBeRestricted => 2018,
184 SolidityErrorCode::UnusedLocalVariable => 2072,
185 SolidityErrorCode::UnusedFunctionParameter => 5667,
186 SolidityErrorCode::ReturnValueOfCallsNotUsed => 9302,
187 SolidityErrorCode::InterfacesExplicitlyVirtual => 5815,
188 SolidityErrorCode::PayableNoReceiveEther => 3628,
189 SolidityErrorCode::ShadowsExistingDeclaration => 2519,
190 SolidityErrorCode::DeclarationSameNameAsAnother => 8760,
191 SolidityErrorCode::UnnamedReturnVariable => 6321,
192 SolidityErrorCode::Unreachable => 5740,
193 SolidityErrorCode::PragmaSolidity => 3420,
194 SolidityErrorCode::TransientStorageUsed => 2394,
195 SolidityErrorCode::TooManyWarnings => 4591,
196 SolidityErrorCode::Other(code) => code,
197 }
198 }
199}
200
201impl fmt::Display for SolidityErrorCode {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 match self.as_str() {
204 Ok(name) => name.fmt(f),
205 Err(code) => code.fmt(f),
206 }
207 }
208}
209
210impl FromStr for SolidityErrorCode {
211 type Err = String;
212
213 fn from_str(s: &str) -> Result<Self, Self::Err> {
214 let code = match s {
215 "license" => Self::SpdxLicenseNotProvided,
216 "constructor-visibility" => Self::VisibilityForConstructorIsIgnored,
217 "code-size" => Self::ContractExceeds24576Bytes,
218 "init-code-size" => Self::ContractInitCodeSizeExceeds49152Bytes,
219 "func-mutability" => Self::FunctionStateMutabilityCanBeRestricted,
220 "unused-var" => Self::UnusedLocalVariable,
221 "unused-param" => Self::UnusedFunctionParameter,
222 "unused-return" => Self::ReturnValueOfCallsNotUsed,
223 "virtual-interfaces" => Self::InterfacesExplicitlyVirtual,
224 "missing-receive-ether" => Self::PayableNoReceiveEther,
225 "shadowing" => Self::ShadowsExistingDeclaration,
226 "same-varname" => Self::DeclarationSameNameAsAnother,
227 "unnamed-return" => Self::UnnamedReturnVariable,
228 "unreachable" => Self::Unreachable,
229 "pragma-solidity" => Self::PragmaSolidity,
230 "transient-storage" => Self::TransientStorageUsed,
231 "too-many-warnings" => Self::TooManyWarnings,
232 _ => return Err(format!("Unknown variant {s}")),
233 };
234
235 Ok(code)
236 }
237}
238
239impl From<u64> for SolidityErrorCode {
240 fn from(code: u64) -> Self {
241 match code {
242 1878 => Self::SpdxLicenseNotProvided,
243 2462 => Self::VisibilityForConstructorIsIgnored,
244 5574 => Self::ContractExceeds24576Bytes,
245 3860 => Self::ContractInitCodeSizeExceeds49152Bytes,
246 2018 => Self::FunctionStateMutabilityCanBeRestricted,
247 2072 => Self::UnusedLocalVariable,
248 5667 => Self::UnusedFunctionParameter,
249 9302 => Self::ReturnValueOfCallsNotUsed,
250 5815 => Self::InterfacesExplicitlyVirtual,
251 3628 => Self::PayableNoReceiveEther,
252 2519 => Self::ShadowsExistingDeclaration,
253 8760 => Self::DeclarationSameNameAsAnother,
254 6321 => Self::UnnamedReturnVariable,
255 5740 => Self::Unreachable,
256 3420 => Self::PragmaSolidity,
257 2394 => Self::TransientStorageUsed,
258 other => Self::Other(other),
259 }
260 }
261}
262
263impl Serialize for SolidityErrorCode {
264 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
265 where
266 S: Serializer,
267 {
268 match self.as_str() {
269 Ok(alias) => serializer.serialize_str(alias),
270 Err(code) => serializer.serialize_u64(code),
271 }
272 }
273}
274
275impl<'de> Deserialize<'de> for SolidityErrorCode {
276 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
277 where
278 D: Deserializer<'de>,
279 {
280 #[derive(Deserialize)]
282 #[serde(untagged)]
283 enum SolCode {
284 Name(String),
285 Code(u64),
286 }
287
288 match SolCode::deserialize(deserializer)? {
289 SolCode::Code(code) => Ok(code.into()),
290 SolCode::Name(name) => name.parse().map_err(serde::de::Error::custom),
291 }
292 }
293}