Skip to main content

foundry_compilers_core/
error.rs

1use semver::Version;
2use std::{
3    io,
4    path::{Path, PathBuf},
5};
6use thiserror::Error;
7
8pub type Result<T, E = SolcError> = std::result::Result<T, E>;
9
10#[allow(unused_macros)]
11#[macro_export]
12macro_rules! format_err {
13    ($($tt:tt)*) => {
14        $crate::error::SolcError::msg(format!($($tt)*))
15    };
16}
17
18#[allow(unused_macros)]
19#[macro_export]
20macro_rules! bail {
21    ($($tt:tt)*) => { return Err(format_err!($($tt)*)) };
22}
23
24/// Various error types
25#[derive(Debug, Error)]
26pub enum SolcError {
27    /// Errors related to the Solc executable itself.
28    #[error("solc exited with {0}\n{1}")]
29    SolcError(std::process::ExitStatus, String),
30    #[error("failed to parse a file: {0}")]
31    ParseError(String),
32    #[error("invalid UTF-8 in Solc output")]
33    InvalidUtf8,
34    #[error("missing pragma from Solidity file")]
35    PragmaNotFound,
36    #[error("could not find Solc version locally or upstream")]
37    VersionNotFound,
38    #[error("checksum mismatch for {file}: expected {expected} found {detected} for {version}")]
39    ChecksumMismatch { version: Version, expected: String, detected: String, file: PathBuf },
40    #[error("checksum not found for {version}")]
41    ChecksumNotFound { version: Version },
42    #[error(transparent)]
43    SemverError(#[from] semver::Error),
44    /// Deserialization error
45    #[error(transparent)]
46    SerdeJson(#[from] serde_json::Error),
47    /// Filesystem IO error
48    #[error(transparent)]
49    Io(#[from] SolcIoError),
50    #[error("file could not be resolved due to broken symlink: {0}")]
51    ResolveBadSymlink(SolcIoError),
52    /// Failed to resolve a file
53    #[error("failed to resolve file: {0}; check configured remappings")]
54    Resolve(SolcIoError),
55    #[error(
56        "file cannot be resolved due to mismatch of file name case: {error}.\nFound existing file: {existing_file:?}\nPlease check the case of the import."
57    )]
58    ResolveCaseSensitiveFileName { error: SolcIoError, existing_file: PathBuf },
59    #[error(
60        "{0}\n\t\
61         --> {1}\n\t\
62         {2}"
63    )]
64    FailedResolveImport(Box<Self>, PathBuf, PathBuf),
65    #[cfg(feature = "svm-solc")]
66    #[error(transparent)]
67    SvmError(#[from] svm::SvmError),
68    #[error("no contracts found at \"{0}\"")]
69    NoContracts(String),
70    /// General purpose message.
71    #[error("{0}")]
72    Message(String),
73
74    #[error("no artifact found for `{}:{}`", .0.display(), .1)]
75    ArtifactNotFound(PathBuf, String),
76
77    #[error(transparent)]
78    Fmt(#[from] std::fmt::Error),
79
80    #[cfg(feature = "project-util")]
81    #[error(transparent)]
82    FsExtra(#[from] fs_extra::error::Error),
83}
84
85impl SolcError {
86    pub fn io(err: io::Error, path: impl Into<PathBuf>) -> Self {
87        SolcIoError::new(err, path).into()
88    }
89
90    /// Create an error from the Solc executable's output.
91    pub fn solc_output(output: &std::process::Output) -> Self {
92        let mut msg = String::from_utf8_lossy(&output.stderr);
93        let mut trimmed = msg.trim();
94        if trimmed.is_empty() {
95            msg = String::from_utf8_lossy(&output.stdout);
96            trimmed = msg.trim();
97            if trimmed.is_empty() {
98                trimmed = "<empty output>";
99            }
100        }
101        Self::SolcError(output.status, trimmed.into())
102    }
103
104    /// General purpose message.
105    pub fn msg(msg: impl std::fmt::Display) -> Self {
106        Self::Message(msg.to_string())
107    }
108}
109
110#[derive(Debug, Error)]
111#[error("\"{}\": {io}", self.path.display())]
112pub struct SolcIoError {
113    io: io::Error,
114    path: PathBuf,
115}
116
117impl SolcIoError {
118    pub fn new(io: io::Error, path: impl Into<PathBuf>) -> Self {
119        Self { io, path: path.into() }
120    }
121
122    /// The path at which the error occurred
123    pub fn path(&self) -> &Path {
124        &self.path
125    }
126
127    /// The underlying `io::Error`
128    pub const fn source(&self) -> &io::Error {
129        &self.io
130    }
131}
132
133impl From<SolcIoError> for io::Error {
134    fn from(err: SolcIoError) -> Self {
135        err.io
136    }
137}