tame_index/
error.rs

1//! Provides the various error types for this crate
2
3#[cfg(feature = "__git")]
4pub use crate::index::git_remote::GitError;
5#[cfg(feature = "local")]
6pub use crate::index::local::LocalRegistryError;
7
8/// The core error type for this library
9#[derive(Debug, thiserror::Error)]
10pub enum Error {
11    /// Failed to deserialize a local cache entry
12    #[error(transparent)]
13    Cache(#[from] CacheError),
14    /// This library assumes utf-8 paths in all cases, a path was provided that
15    /// was not valid utf-8
16    #[error("unable to use non-utf8 path {:?}", .0)]
17    NonUtf8Path(std::path::PathBuf),
18    /// An environment variable was located, but had a non-utf8 value
19    #[error("environment variable {} has a non-utf8 value", .0)]
20    NonUtf8EnvVar(std::borrow::Cow<'static, str>),
21    /// A user-provided string was not a valid crate name
22    #[error(transparent)]
23    InvalidKrateName(#[from] InvalidKrateName),
24    /// The user specified a registry name that did not exist in any searched
25    /// .cargo/config.toml
26    #[error("registry '{}' was not located in any .cargo/config.toml", .0)]
27    UnknownRegistry(String),
28    /// An I/O error
29    #[error(transparent)]
30    Io(#[from] std::io::Error),
31    /// An I/O error occurred trying to access a specific path
32    #[error("I/O operation failed for path '{}': {}", .1, .0)]
33    IoPath(#[source] std::io::Error, crate::PathBuf),
34    /// A user provided URL was invalid
35    #[error(transparent)]
36    InvalidUrl(#[from] InvalidUrl),
37    /// Failed to de/serialize JSON
38    #[error(transparent)]
39    Json(#[from] serde_json::Error),
40    /// Failed to deserialize TOML
41    #[error(transparent)]
42    Toml(#[from] Box<toml_span::Error>),
43    /// An index entry did not contain any versions
44    #[error("index entry contained no versions for the crate")]
45    NoCrateVersions,
46    /// Failed to handle an HTTP response or request
47    #[error(transparent)]
48    Http(#[from] HttpError),
49    /// An error occurred doing a git operation
50    #[cfg(feature = "__git")]
51    #[error(transparent)]
52    Git(#[from] GitError),
53    /// Failed to parse a semver version or requirement
54    #[error(transparent)]
55    Semver(#[from] semver::Error),
56    /// A local registry is invalid
57    #[cfg(feature = "local")]
58    #[error(transparent)]
59    Local(#[from] LocalRegistryError),
60    /// Failed to lock a file
61    #[error(transparent)]
62    Lock(#[from] crate::utils::flock::FileLockError),
63}
64
65impl From<std::path::PathBuf> for Error {
66    fn from(p: std::path::PathBuf) -> Self {
67        Self::NonUtf8Path(p)
68    }
69}
70
71/// Various kinds of reserved names disallowed by cargo
72#[derive(Debug, Copy, Clone)]
73pub enum ReservedNameKind {
74    /// The name is a Rust keyword
75    Keyword,
76    /// The name conflicts with a cargo artifact directory
77    Artifact,
78    /// The name has a special meaning on Windows
79    Windows,
80    /// The name conflicts with a Rust std library name
81    Standard,
82}
83
84impl std::fmt::Display for ReservedNameKind {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        match self {
87            Self::Keyword => f.write_str("rustlang keyword"),
88            Self::Artifact => f.write_str("cargo artifact"),
89            Self::Windows => f.write_str("windows reserved"),
90            Self::Standard => f.write_str("rustlang std library"),
91        }
92    }
93}
94
95/// Errors that can occur when validating a crate name
96#[derive(Debug, thiserror::Error)]
97pub enum InvalidKrateName {
98    /// The name had an invalid length
99    #[error("crate name had an invalid length of '{0}'")]
100    InvalidLength(usize),
101    /// The name contained an invalid character
102    #[error("invalid character '{invalid}` @ {index}")]
103    InvalidCharacter {
104        /// The invalid character
105        invalid: char,
106        /// The index of the character in the provided string
107        index: usize,
108    },
109    /// The name was one of the reserved names disallowed by cargo
110    #[error("the name '{reserved}' is reserved as '{kind}`")]
111    ReservedName {
112        /// The name that was reserved
113        reserved: &'static str,
114        /// The kind of the reserved name
115        kind: ReservedNameKind,
116    },
117}
118
119/// An error pertaining to a bad URL provided to the API
120#[derive(Debug, thiserror::Error)]
121#[error("the url '{url}' is invalid")]
122pub struct InvalidUrl {
123    /// The invalid url
124    pub url: String,
125    /// The reason it is invalid
126    pub source: InvalidUrlError,
127}
128
129/// The specific reason for the why the URL is invalid
130#[derive(Debug, thiserror::Error)]
131pub enum InvalidUrlError {
132    /// Sparse HTTP registry urls must be of the form `sparse+http(s)://`
133    #[error("sparse indices require the use of a url that starts with `sparse+http`")]
134    MissingSparse,
135    /// The `<modifier>+<scheme>://` is not supported
136    #[error("the scheme modifier is unknown")]
137    UnknownSchemeModifier,
138    /// Unable to find the `<scheme>://`
139    #[error("the scheme is missing")]
140    MissingScheme,
141    /// Attempted to construct a git index with a sparse URL
142    #[error("attempted to create a git index for a sparse URL")]
143    SparseForGit,
144}
145
146/// Errors related to a local index cache
147#[derive(Debug, thiserror::Error)]
148pub enum CacheError {
149    /// The cache entry is malformed
150    #[error("the cache entry is malformed")]
151    InvalidCacheEntry,
152    /// The cache version is old
153    #[error("the cache entry is an old, unsupported version")]
154    OutdatedCacheVersion,
155    /// The cache version is newer than the version supported by this crate
156    #[error("the cache entry is an unknown version, possibly written by a newer cargo version")]
157    UnknownCacheVersion,
158    /// The index version is newer than the version supported by this crate
159    #[error(
160        "the cache entry's index version is unknown, possibly written by a newer cargo version"
161    )]
162    UnknownIndexVersion,
163    /// The revision in the cache entry did match the requested revision
164    ///
165    /// This can occur when a git index is fetched and a newer revision is pulled
166    /// from the remote index, invalidating all local cache entries
167    #[error("the cache entry's revision does not match the current revision")]
168    OutdatedRevision,
169    /// A crate version in the cache file was malformed
170    #[error("a specific version in the cache entry is malformed")]
171    InvalidCrateVersion,
172}
173
174/// Errors related to HTTP requests or responses
175#[derive(Debug, thiserror::Error)]
176pub enum HttpError {
177    /// A [`reqwest::Error`]
178    #[cfg(any(feature = "sparse", feature = "local-builder"))]
179    #[error(transparent)]
180    Reqwest(#[from] reqwest::Error),
181    /// A status code was received that indicates user error, or possibly a
182    /// remote index that does not follow the protocol supported by this crate
183    #[error("status code '{code}': {msg}")]
184    StatusCode {
185        /// The status code
186        code: http::StatusCode,
187        /// The reason the status code raised an error
188        msg: &'static str,
189    },
190    /// A [`http::Error`]
191    #[error(transparent)]
192    Http(#[from] http::Error),
193    /// A string could not be parsed as a valid header value
194    #[error(transparent)]
195    InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
196    /// Unable to complete an async request for an `AsyncRemoteSparseIndex` within
197    /// the user allotted time
198    #[error("request could not be completed in the allotted timeframe")]
199    Timeout,
200}