cli/
error.rs

1use std::cmp::PartialEq;
2use std::string::ToString;
3
4#[derive(
5    strum_macros::AsRefStr,
6    strum_macros::Display,
7    strum_macros::EnumDiscriminants,
8    strum_macros::IntoStaticStr,
9    Debug,
10)]
11#[repr(u16)]
12pub enum CargoMakeError {
13    #[strum(
14        to_string = "A cycle between different env variables has been detected \
15    (E001, see: https://github.com/sagiegurari/cargo-make#e001 for more information). {0}"
16    )]
17    EnvVarCycle(String) = 100,
18
19    #[strum(to_string = "Detected cycle while resolving alias {0}: {1}")]
20    AliasCycle(String, String) = 101,
21
22    #[strum(to_string = "Circular reference found for task: {0:#?}")]
23    CircularReference(String) = 102,
24
25    #[strum(to_string = "Unable to run, minimum required version is: {0}")]
26    VersionTooOld(String) = 103,
27
28    #[strum(to_string = "Error while executing command, unable to extract exit code.")]
29    ExitCodeValidation = 104,
30
31    #[strum(to_string = "Error while executing command, exit code: {0}")]
32    ExitCodeError(i32) = 105,
33
34    #[strum(to_string = "Unable to parse internal descriptor: {0}")]
35    DescriptorParseFailed(String) = 106,
36
37    #[strum(to_string = "Unable to parse external file: {0:#?}, {1}")]
38    ParseFileFailed(String, String) = 107,
39
40    #[strum(to_string = "{0}")]
41    Arity(&'static str) = 108,
42
43    #[strum(to_string = "{0}")]
44    MethodCallRestriction(&'static str) = 109,
45
46    #[strum(to_string = "Task {0:#?} is {1}")]
47    TaskIs(String, &'static str) = 110,
48
49    #[strum(to_string = "{0}")]
50    NotFound(String) = 404,
51
52    // ************************
53    // * Library level errors *
54    // ************************
55    #[strum(to_string = "`std::io::Error` error. {error:?}")]
56    StdIoError { error: std::io::Error } = 700,
57
58    #[strum(to_string = "`std::fmt::Error` error. {error:?}")]
59    StdFmtError { error: std::fmt::Error } = 709,
60
61    #[strum(to_string = "{0:?}")]
62    ExitCode(std::process::ExitCode) = 710,
63
64    #[strum(to_string = "`toml::de::Error` error. {error:?}")]
65    TomlDeError { error: toml::de::Error } = 720,
66
67    #[strum(to_string = "`fsio::error::FsIOError` error. {error:?}")]
68    FsIOError { error: fsio::error::FsIOError } = 730,
69
70    #[strum(to_string = "`cliparser::types::ParserError` error. {error:?}")]
71    ParserError {
72        error: cliparser::types::ParserError,
73    } = 731,
74}
75
76impl CargoMakeError {
77    fn discriminant(&self) -> u16 {
78        unsafe { *(self as *const Self as *const u16) }
79    }
80}
81
82impl From<std::io::Error> for CargoMakeError {
83    fn from(error: std::io::Error) -> Self {
84        Self::StdIoError { error }
85    }
86}
87
88impl From<std::fmt::Error> for CargoMakeError {
89    fn from(error: std::fmt::Error) -> Self {
90        Self::StdFmtError { error }
91    }
92}
93
94impl From<toml::de::Error> for CargoMakeError {
95    fn from(error: toml::de::Error) -> Self {
96        Self::TomlDeError { error }
97    }
98}
99
100impl From<fsio::error::FsIOError> for CargoMakeError {
101    fn from(error: fsio::error::FsIOError) -> Self {
102        Self::FsIOError { error }
103    }
104} // ::ParserError
105
106impl From<cliparser::types::ParserError> for CargoMakeError {
107    fn from(error: cliparser::types::ParserError) -> Self {
108        Self::ParserError { error }
109    }
110}
111
112impl From<std::process::ExitCode> for CargoMakeError {
113    fn from(error: std::process::ExitCode) -> Self {
114        Self::ExitCode(error)
115    }
116}
117
118impl std::process::Termination for CargoMakeError {
119    fn report(self) -> std::process::ExitCode {
120        if let CargoMakeError::ExitCode(exit_code) = self {
121            return exit_code;
122        }
123        let status_code = self.discriminant();
124        if status_code > u8::MAX as u16 {
125            eprintln!("exit code {}", status_code);
126            std::process::ExitCode::FAILURE
127        } else {
128            std::process::ExitCode::from(status_code as u8)
129        }
130    }
131}
132
133pub enum SuccessOrCargoMakeError<T> {
134    Ok(T),
135    Err(CargoMakeError),
136}
137
138impl<T> From<Result<T, CargoMakeError>> for SuccessOrCargoMakeError<T> {
139    fn from(value: Result<T, CargoMakeError>) -> Self {
140        match value {
141            Ok(val) => SuccessOrCargoMakeError::Ok(val),
142            Err(error) => SuccessOrCargoMakeError::Err(error),
143        }
144    }
145}
146
147// Can't use `Result` because
148// [E0117] Only traits defined in the current crate can be implemented for arbitrary types
149impl<T: std::any::Any> std::process::Termination for SuccessOrCargoMakeError<T> {
150    fn report(self) -> std::process::ExitCode {
151        const PROCESS_EXIT_CODE: fn(i32) -> std::process::ExitCode = |e: i32| {
152            if e > u8::MAX as i32 {
153                eprintln!("exit code {}", e);
154                std::process::ExitCode::FAILURE
155            } else {
156                std::process::ExitCode::from(e as u8)
157            }
158        };
159
160        match self {
161            SuccessOrCargoMakeError::Ok(e)
162                if std::any::TypeId::of::<T>()
163                    == std::any::TypeId::of::<std::process::ExitCode>() =>
164            {
165                *(&e as &dyn std::any::Any)
166                    .downcast_ref::<std::process::ExitCode>()
167                    .unwrap()
168            }
169            SuccessOrCargoMakeError::Ok(_) => std::process::ExitCode::SUCCESS,
170            SuccessOrCargoMakeError::Err(err) => match err {
171                CargoMakeError::StdIoError { error } if error.raw_os_error().is_some() => {
172                    let e = unsafe { error.raw_os_error().unwrap_unchecked() };
173                    eprintln!("{}", e.to_string());
174                    PROCESS_EXIT_CODE(e)
175                }
176                CargoMakeError::ExitCode(error) => error,
177                _ => {
178                    eprintln!("{}", err.to_string());
179                    PROCESS_EXIT_CODE(err.discriminant() as i32)
180                }
181            },
182        }
183    }
184}