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 TransferDeprecated,
145 NatspecMemorySafeAssemblyDeprecated,
147 Other(u64),
149}
150
151impl SolidityErrorCode {
152 pub fn as_str(&self) -> Result<&'static str, u64> {
156 let s = match self {
157 Self::SpdxLicenseNotProvided => "license",
158 Self::VisibilityForConstructorIsIgnored => "constructor-visibility",
159 Self::ContractExceeds24576Bytes => "code-size",
160 Self::ContractInitCodeSizeExceeds49152Bytes => "init-code-size",
161 Self::FunctionStateMutabilityCanBeRestricted => "func-mutability",
162 Self::UnusedLocalVariable => "unused-var",
163 Self::UnusedFunctionParameter => "unused-param",
164 Self::ReturnValueOfCallsNotUsed => "unused-return",
165 Self::InterfacesExplicitlyVirtual => "virtual-interfaces",
166 Self::PayableNoReceiveEther => "missing-receive-ether",
167 Self::ShadowsExistingDeclaration => "shadowing",
168 Self::DeclarationSameNameAsAnother => "same-varname",
169 Self::UnnamedReturnVariable => "unnamed-return",
170 Self::Unreachable => "unreachable",
171 Self::PragmaSolidity => "pragma-solidity",
172 Self::TransientStorageUsed => "transient-storage",
173 Self::TooManyWarnings => "too-many-warnings",
174 Self::TransferDeprecated => "transfer-deprecated",
175 Self::NatspecMemorySafeAssemblyDeprecated => "natspec-memory-safe-assembly-deprecated",
176 Self::Other(code) => return Err(*code),
177 };
178 Ok(s)
179 }
180}
181
182impl From<SolidityErrorCode> for u64 {
183 fn from(code: SolidityErrorCode) -> Self {
184 match code {
185 SolidityErrorCode::SpdxLicenseNotProvided => 1878,
186 SolidityErrorCode::VisibilityForConstructorIsIgnored => 2462,
187 SolidityErrorCode::ContractExceeds24576Bytes => 5574,
188 SolidityErrorCode::ContractInitCodeSizeExceeds49152Bytes => 3860,
189 SolidityErrorCode::FunctionStateMutabilityCanBeRestricted => 2018,
190 SolidityErrorCode::UnusedLocalVariable => 2072,
191 SolidityErrorCode::UnusedFunctionParameter => 5667,
192 SolidityErrorCode::ReturnValueOfCallsNotUsed => 9302,
193 SolidityErrorCode::InterfacesExplicitlyVirtual => 5815,
194 SolidityErrorCode::PayableNoReceiveEther => 3628,
195 SolidityErrorCode::ShadowsExistingDeclaration => 2519,
196 SolidityErrorCode::DeclarationSameNameAsAnother => 8760,
197 SolidityErrorCode::UnnamedReturnVariable => 6321,
198 SolidityErrorCode::Unreachable => 5740,
199 SolidityErrorCode::PragmaSolidity => 3420,
200 SolidityErrorCode::TransientStorageUsed => 2394,
201 SolidityErrorCode::TooManyWarnings => 4591,
202 SolidityErrorCode::TransferDeprecated => 9207,
203 SolidityErrorCode::NatspecMemorySafeAssemblyDeprecated => 2424,
204 SolidityErrorCode::Other(code) => code,
205 }
206 }
207}
208
209impl fmt::Display for SolidityErrorCode {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 match self.as_str() {
212 Ok(name) => name.fmt(f),
213 Err(code) => code.fmt(f),
214 }
215 }
216}
217
218impl FromStr for SolidityErrorCode {
219 type Err = String;
220
221 fn from_str(s: &str) -> Result<Self, Self::Err> {
222 let code = match s {
223 "license" => Self::SpdxLicenseNotProvided,
224 "constructor-visibility" => Self::VisibilityForConstructorIsIgnored,
225 "code-size" => Self::ContractExceeds24576Bytes,
226 "init-code-size" => Self::ContractInitCodeSizeExceeds49152Bytes,
227 "func-mutability" => Self::FunctionStateMutabilityCanBeRestricted,
228 "unused-var" => Self::UnusedLocalVariable,
229 "unused-param" => Self::UnusedFunctionParameter,
230 "unused-return" => Self::ReturnValueOfCallsNotUsed,
231 "virtual-interfaces" => Self::InterfacesExplicitlyVirtual,
232 "missing-receive-ether" => Self::PayableNoReceiveEther,
233 "shadowing" => Self::ShadowsExistingDeclaration,
234 "same-varname" => Self::DeclarationSameNameAsAnother,
235 "unnamed-return" => Self::UnnamedReturnVariable,
236 "unreachable" => Self::Unreachable,
237 "pragma-solidity" => Self::PragmaSolidity,
238 "transient-storage" => Self::TransientStorageUsed,
239 "too-many-warnings" => Self::TooManyWarnings,
240 "transfer-deprecated" => Self::TransferDeprecated,
241 "natspec-memory-safe-assembly-deprecated" => Self::NatspecMemorySafeAssemblyDeprecated,
242 _ => return Err(format!("Unknown variant {s}")),
243 };
244
245 Ok(code)
246 }
247}
248
249impl From<u64> for SolidityErrorCode {
250 fn from(code: u64) -> Self {
251 match code {
252 1878 => Self::SpdxLicenseNotProvided,
253 2462 => Self::VisibilityForConstructorIsIgnored,
254 5574 => Self::ContractExceeds24576Bytes,
255 3860 => Self::ContractInitCodeSizeExceeds49152Bytes,
256 2018 => Self::FunctionStateMutabilityCanBeRestricted,
257 2072 => Self::UnusedLocalVariable,
258 5667 => Self::UnusedFunctionParameter,
259 9302 => Self::ReturnValueOfCallsNotUsed,
260 5815 => Self::InterfacesExplicitlyVirtual,
261 3628 => Self::PayableNoReceiveEther,
262 2519 => Self::ShadowsExistingDeclaration,
263 8760 => Self::DeclarationSameNameAsAnother,
264 6321 => Self::UnnamedReturnVariable,
265 5740 => Self::Unreachable,
266 3420 => Self::PragmaSolidity,
267 2394 => Self::TransientStorageUsed,
268 4591 => Self::TooManyWarnings,
269 9207 => Self::TransferDeprecated,
270 2424 => Self::NatspecMemorySafeAssemblyDeprecated,
271 other => Self::Other(other),
272 }
273 }
274}
275
276impl Serialize for SolidityErrorCode {
277 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
278 where
279 S: Serializer,
280 {
281 match self.as_str() {
282 Ok(alias) => serializer.serialize_str(alias),
283 Err(code) => serializer.serialize_u64(code),
284 }
285 }
286}
287
288impl<'de> Deserialize<'de> for SolidityErrorCode {
289 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
290 where
291 D: Deserializer<'de>,
292 {
293 #[derive(Deserialize)]
295 #[serde(untagged)]
296 enum SolCode {
297 Name(String),
298 Code(u64),
299 }
300
301 match SolCode::deserialize(deserializer)? {
302 SolCode::Code(code) => Ok(code.into()),
303 SolCode::Name(name) => name.parse().map_err(serde::de::Error::custom),
304 }
305 }
306}