oro_client/
error.rs

1use miette::{Diagnostic, NamedSource, SourceOffset};
2use reqwest::Url;
3use thiserror::Error;
4
5#[derive(Debug)]
6pub struct Response(Option<String>);
7
8#[derive(Debug, Error, Diagnostic)]
9pub enum OroClientError {
10    /// An invalid URL was provided.
11    #[error(transparent)]
12    #[diagnostic(code(oro_client::url_parse_error), url(docsrs))]
13    UrlParseError(#[from] url::ParseError),
14
15    /// The package was not found in the registry.
16    ///
17    /// Make sure the package name is spelled correctly and that you've
18    /// configured the right registry to fetch it from.
19    #[error("Package `{1}` was not found in registry {0}.")]
20    #[diagnostic(code(oro_client::package_not_found), url(docsrs))]
21    PackageNotFound(Url, String),
22
23    /// Got some bad JSON we couldn't parse.
24    #[error("Received some unexpected JSON. Unable to parse.")]
25    #[diagnostic(code(oro_client::bad_json), url(docsrs))]
26    BadJson {
27        source: serde_json::Error,
28        url: String,
29        #[source_code]
30        json: NamedSource,
31        #[label("here")]
32        err_loc: (usize, usize),
33    },
34
35    /// A generic request error happened while making a request. Refer to the
36    /// error message for more details.
37    #[error(transparent)]
38    #[diagnostic(code(oro_client::request_error), url(docsrs))]
39    RequestError(#[from] reqwest::Error),
40
41    /// Recived unexpected response.
42    #[error("Received unexpected response. \n {0}")]
43    #[diagnostic(code(oro_client::response_error), url(docsrs))]
44    ResponseError(Response),
45
46    /// No such user.
47    #[error("No such user. (provided username: {0})")]
48    #[diagnostic(code(oro_client::no_such_user_error), url(docsrs))]
49    NoSuchUserError(String),
50
51    /// Incorrect or missing password.
52    #[error("Incorrect or missing password.")]
53    #[diagnostic(code(oro_client::incorrect_password_error), url(docsrs))]
54    IncorrectPasswordError,
55
56    /// Unable to authenticate, your authentication token seems to be invalid.
57    #[error("Unable to authenticate, your authentication token seems to be invalid.")]
58    #[diagnostic(code(oro_client::invalid_token_error), url(docsrs))]
59    InvalidTokenError,
60
61    /// This operation requires a one-time password from your authenticator.
62    #[error("This operation requires a one-time password from your authenticator.")]
63    #[diagnostic(code(oro_client::otp_required_error), url(docsrs))]
64    OTPRequiredError,
65
66    /// A generic request middleware error happened while making a request.
67    /// Refer to the error message for more details.
68    #[error(transparent)]
69    #[diagnostic(code(oro_client::request_middleware_error), url(docsrs))]
70    RequestMiddlewareError(#[from] reqwest_middleware::Error),
71
72    /// An error during reading the configuration
73    #[error("Could not parse credentials config. {0}")]
74    #[diagnostic(code(oro_client::credentials_config_error), url(docsrs))]
75    CredentialsConfigError(String),
76
77    /// Auth string did not include a username when decoded.
78    #[error("Auth string did not include a username when decoded.")]
79    #[diagnostic(code(oro_client::auth_string_missing_username), url(docsrs))]
80    AuthStringMissingUsername(String),
81
82    /// Failed to decode base64.
83    #[error(transparent)]
84    #[diagnostic(code(oro_client::base64_decode_error), url(docsrs))]
85    Base64DecodeError(#[from] base64::DecodeError),
86}
87
88impl OroClientError {
89    pub fn from_json_err(err: serde_json::Error, url: String, json: String) -> Self {
90        // These json strings can get VERY LONG and miette doesn't (yet?)
91        // support any "windowing" mechanism for displaying stuff, so we have
92        // to manually shorten the string to only the relevant bits and
93        // translate the spans accordingly.
94        let err_offset = SourceOffset::from_location(&json, err.line(), err.column());
95        let json_len = json.len();
96        let local_offset = err_offset.offset().saturating_sub(40);
97        let local_len = std::cmp::min(40, json_len - err_offset.offset());
98        let snipped_json = json[local_offset..err_offset.offset() + local_len].to_string();
99        Self::BadJson {
100            source: err,
101            url: url.clone(),
102            json: NamedSource::new(url, snipped_json),
103            err_loc: (err_offset.offset() - local_offset, 0),
104        }
105    }
106}
107
108impl From<Option<String>> for Response {
109    fn from(value: Option<String>) -> Self {
110        Response(value)
111    }
112}
113
114impl std::fmt::Display for Response {
115    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
116        write!(
117            f,
118            "{}",
119            if let Some(response) = &self.0 {
120                response
121            } else {
122                ""
123            }
124        )
125    }
126}