Skip to main content

cargo_msrv/
error.rs

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;