1use camino::Utf8PathBuf;
2use owo_colors::OwoColorize;
3use std::env;
4use std::ffi::OsString;
5use std::io;
6use std::path::PathBuf;
7use std::string::FromUtf8Error;
8
9use crate::cli::rust_releases_opts::{ParseEditionError, ParseEditionOrVersionError};
10use crate::log_level::ParseLogLevelError;
11use crate::manifest::ManifestParseError;
12use crate::manifest::bare_version::{BareVersion, NoVersionMatchesManifestMsrvError};
13use rust_releases::Release;
14
15use crate::sub_command::{show, verify};
16
17pub(crate) type TResult<T> = Result<T, CargoMSRVError>;
18
19#[derive(Debug, thiserror::Error)]
20pub enum CargoMSRVError {
21 #[error("Unable to parse minimum rust version: {0}")]
22 BareVersionParse(#[from] crate::manifest::bare_version::Error),
23
24 #[error(transparent)]
25 CargoMetadata(#[from] cargo_metadata::Error),
26
27 #[error("The default host triple (target) could not be found.")]
28 DefaultHostTripleNotFound,
29
30 #[error(transparent)]
31 Env(#[from] env::VarError),
32
33 #[error("{0}")]
34 GenericMessage(String),
35
36 #[error(transparent)]
37 Io(#[from] IoError),
38
39 #[error("{0}")]
40 InvalidConfig(String),
41
42 #[error(transparent)]
43 InvalidRustVersionNumber(#[from] std::num::ParseIntError),
44
45 #[error(transparent)]
46 InvalidMsrvSet(#[from] InvalidMsrvSetError),
47
48 #[error(transparent)]
49 InvalidUTF8(#[from] FromUtf8Error),
50
51 #[error(transparent)]
52 LockfileHandler(#[from] LockfileHandlerError),
53
54 #[error(transparent)]
55 ManifestParseError(#[from] ManifestParseError),
56
57 #[error("No crate root found for given crate")]
58 NoCrateRootFound,
59
60 #[error(transparent)]
61 NoToolchainsToTry(#[from] NoToolchainsToTryError),
62
63 #[error("Unable to set MSRV for workspace, try setting it for individual packages instead.")]
64 WorkspaceFound,
65
66 #[error(transparent)]
67 NoVersionMatchesManifestMSRV(#[from] NoVersionMatchesManifestMsrvError),
68
69 #[error("Unable to find key 'package.rust-version' (or 'package.metadata.msrv') in '{0}'")]
70 NoMSRVKeyInCargoToml(Utf8PathBuf),
71
72 #[error(transparent)]
73 ParseEdition(#[from] ParseEditionError),
74
75 #[error(transparent)]
76 ParseEditionOrVersion(#[from] ParseEditionOrVersionError),
77
78 #[error(transparent)]
79 ParseLogLevel(#[from] ParseLogLevelError),
80
81 #[error("Unable to parse Cargo.toml: {0}")]
82 ParseToml(#[from] toml_edit::TomlError),
83
84 #[error(transparent)]
85 RustReleasesSource(#[from] rust_releases::RustChangelogError),
86
87 #[error(transparent)]
88 #[cfg(feature = "rust-releases-dist-source")]
89 RustReleasesRustDistSource(#[from] rust_releases::RustDistError),
90
91 #[error("Unable to parse rust-releases source from '{0}'")]
92 RustReleasesSourceParseError(String),
93
94 #[error("There are no Rust releases in the rust-releases index")]
95 RustReleasesEmptyReleaseSet,
96
97 #[error(transparent)]
98 RustupError(#[from] RustupError),
99
100 #[error("Check toolchain (with `rustup run <toolchain> <command>`) failed.")]
101 RustupRunWithCommandFailed,
102
103 #[error(transparent)]
104 SemverError(#[from] rust_releases::semver::Error),
105
106 #[error(transparent)]
107 SetMsrv(#[from] SetMsrvError),
108
109 #[error("Unable to print event output")]
110 Storyteller,
111
112 #[error(transparent)]
113 SubCommandVerify(#[from] verify::Error),
114
115 #[error(transparent)]
116 SubCommandShow(#[from] show::Error),
117
118 #[error(transparent)]
119 SystemTime(#[from] std::time::SystemTimeError),
120
121 #[error(
122 "The given toolchain could not be found. Run `rustup toolchain list` for an overview of installed toolchains."
123 )]
124 ToolchainNotInstalled,
125
126 #[error(
127 "The given target could not be found. Run `rustup target list` for an overview of available toolchains."
128 )]
129 UnknownTarget,
130
131 #[error("Unable to get or store the channel manifest on disk.")]
132 UnableToCacheChannelManifest,
133
134 #[error(
135 r#"Unable to find a Minimum Supported Rust Version (MSRV).
136
137If you think this result is erroneous, please run: `{command}` manually.
138
139If the above does succeed, or you think cargo-msrv errored in another way, please feel free to
140report the issue at: https://github.com/foresterre/cargo-msrv/issues
141
142Thank you in advance!"#
143 )]
144 UnableToFindAnyGoodVersion { command: String },
145
146 #[error("Unable to parse the CLI arguments. Use `cargo msrv help` for more info.")]
147 UnableToParseCliArgs,
148
149 #[error("The Rust stable version could not be parsed from the stable channel manifest.")]
150 UnableToParseRustVersion,
151
152 #[error("Unable to run the check command: '{}' at '{}'", &command, &cwd)]
153 UnableToRunCheck { command: String, cwd: Utf8PathBuf },
154
155 #[error(transparent)]
156 Path(#[from] PathError),
157}
158
159impl From<String> for CargoMSRVError {
160 fn from(s: String) -> Self {
161 Self::GenericMessage(s)
162 }
163}
164
165#[derive(Debug, thiserror::Error)]
166#[error("IO error: '{error}'. caused by: '{source}'.")]
167pub struct IoError {
168 pub error: io::Error,
169 pub source: IoErrorSource,
170}
171
172#[derive(Debug, thiserror::Error)]
173pub enum IoErrorSource {
174 #[error("Unable to determine current working directory")]
175 CurrentDir,
176
177 #[error("Unable to open file '{0}'")]
178 OpenFile(Utf8PathBuf),
179
180 #[error("Unable to read file '{0}'")]
181 ReadFile(Utf8PathBuf),
182
183 #[error("Unable to write file '{0}'")]
184 WriteFile(Utf8PathBuf),
185
186 #[error("Unable to remove file '{0}'")]
187 RemoveFile(Utf8PathBuf),
188
189 #[error("Unable to rename file '{0}'")]
190 RenameFile(Utf8PathBuf),
191
192 #[error("Unable to spawn process '{0:?}'")]
193 SpawnProcess(OsString),
194
195 #[error("Unable to collect output from '{0:?}', or process did not terminate properly")]
196 WaitForProcessAndCollectOutput(OsString),
197}
198
199#[derive(Debug, thiserror::Error)]
200pub enum SetMsrvError {
201 #[error(
202 "Unable to set the MSRV in the 'package.metadata' table: 'package.metadata' is not a table"
203 )]
204 NotATable,
205}
206
207#[derive(Debug, thiserror::Error)]
208#[error("No Rust releases to check: the filtered search space is empty.{}",
209 inner.as_ref().map(|clues| format!("{}", clues)).unwrap_or_default(),
210)]
211pub struct NoToolchainsToTryError {
212 inner: Option<NoToolchainToTryClues>,
213}
214
215impl NoToolchainsToTryError {
216 pub fn new_empty() -> Self {
217 Self { inner: None }
218 }
219
220 pub fn with_clues(user_min: Option<BareVersion>, user_max: Option<BareVersion>) -> Self {
221 Self {
222 inner: Some(NoToolchainToTryClues {
223 min: user_min,
224 max: user_max,
225 }),
226 }
227 }
228
229 pub fn has_clues(&self) -> bool {
230 self.inner.is_some()
231 }
232}
233
234#[derive(Debug, thiserror::Error)]
235#[error(" Search space limited by user to min Rust '{}', and max Rust '{}'",
236 min.as_ref().map(|s| format!("{}", s)).unwrap_or_else(|| "<not overridden>".to_string()),
237 max.as_ref().map(|s| format!("{}", s)).unwrap_or_else(|| "<not overridden>".to_string()),
238)]
239pub struct NoToolchainToTryClues {
240 min: Option<BareVersion>,
241 max: Option<BareVersion>,
242}
243
244#[derive(Debug, thiserror::Error)]
245#[error("No Rust releases match input '{}' (search space: [{}])",
246 input,
247 search_space.iter().map(|r| r.version().to_string()).collect::<Vec<_>>().join(", ") )
248]
249pub struct InvalidMsrvSetError {
250 pub(crate) input: BareVersion,
251 pub(crate) search_space: Vec<Release>,
252}
253
254impl<T> From<storyteller::EventReporterError<T>> for CargoMSRVError {
255 fn from(_: storyteller::EventReporterError<T>) -> Self {
256 CargoMSRVError::Storyteller
257 }
258}
259
260#[derive(Debug, thiserror::Error)]
261#[error(transparent)]
262pub enum RustupError {
263 Install(#[from] RustupInstallError),
264 AddComponent(#[from] RustupAddComponentError),
265 AddTarget(#[from] RustupAddTargetError),
266}
267
268#[derive(Debug, thiserror::Error)]
269#[error(
270 "Unable to install toolchain '{}', rustup reported:\n {}",
271 toolchain_spec,
272 stderr.trim_end().lines().collect::<Vec<_>>().join("\n ").dimmed()
273)]
274pub struct RustupInstallError {
275 pub toolchain_spec: String,
276 pub stderr: String,
277}
278
279#[derive(Debug, thiserror::Error)]
280#[error(
281 "Unable to add components '{}' to toolchain '{}', rustup reported:\n {}",
282 components,
283 toolchain_spec,
284 stderr.trim_end().lines().collect::<Vec<_>>().join("\n ").dimmed()
285)]
286pub struct RustupAddComponentError {
287 pub components: String,
288 pub toolchain_spec: String,
289 pub stderr: String,
290}
291
292#[derive(Debug, thiserror::Error)]
293#[error(
294 "Unable to add target '{}' to toolchain '{}', rustup reported:\n {}",
295 targets,
296 toolchain_spec,
297 stderr.trim_end().lines().collect::<Vec<_>>().join("\n ").dimmed()
298)]
299pub struct RustupAddTargetError {
300 pub targets: String,
301 pub toolchain_spec: String,
302 pub stderr: String,
303}
304
305#[derive(Debug, thiserror::Error)]
306pub enum PathError {
307 #[error("'{}' does not exist", .0.display())]
308 DoesNotExist(PathBuf),
309
310 #[error("No parent directory for '{}'", .0.display())]
311 NoParent(PathBuf),
312
313 #[error(transparent)]
314 InvalidUtf8(#[from] InvalidUtf8Error),
315}
316
317#[derive(Debug, thiserror::Error)]
318#[error(transparent)]
319pub struct InvalidUtf8Error {
320 error: Utf8PathErrorInner,
321}
322
323impl From<camino::FromPathError> for InvalidUtf8Error {
324 fn from(value: camino::FromPathError) -> Self {
325 Self {
326 error: Utf8PathErrorInner::FromPath(value),
327 }
328 }
329}
330
331impl From<camino::FromPathBufError> for InvalidUtf8Error {
332 fn from(value: camino::FromPathBufError) -> Self {
333 Self {
334 error: Utf8PathErrorInner::FromPathBuf(value),
335 }
336 }
337}
338
339#[derive(Debug, thiserror::Error)]
340enum Utf8PathErrorInner {
341 #[error("Path contains non UTF-8 characters")]
342 FromPath(camino::FromPathError),
343 #[error("Path contains non UTF-8 characters (path: '{}')", .0.as_path().display())]
344 FromPathBuf(camino::FromPathBufError),
345}
346
347#[derive(Debug, thiserror::Error)]
348#[error("Unable to set cleanup handler for lockfile")]
349pub struct LockfileHandlerError;