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 #[error(transparent)]
12 #[diagnostic(code(oro_client::url_parse_error), url(docsrs))]
13 UrlParseError(#[from] url::ParseError),
14
15 #[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 #[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 #[error(transparent)]
38 #[diagnostic(code(oro_client::request_error), url(docsrs))]
39 RequestError(#[from] reqwest::Error),
40
41 #[error("Received unexpected response. \n {0}")]
43 #[diagnostic(code(oro_client::response_error), url(docsrs))]
44 ResponseError(Response),
45
46 #[error("No such user. (provided username: {0})")]
48 #[diagnostic(code(oro_client::no_such_user_error), url(docsrs))]
49 NoSuchUserError(String),
50
51 #[error("Incorrect or missing password.")]
53 #[diagnostic(code(oro_client::incorrect_password_error), url(docsrs))]
54 IncorrectPasswordError,
55
56 #[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 #[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 #[error(transparent)]
69 #[diagnostic(code(oro_client::request_middleware_error), url(docsrs))]
70 RequestMiddlewareError(#[from] reqwest_middleware::Error),
71
72 #[error("Could not parse credentials config. {0}")]
74 #[diagnostic(code(oro_client::credentials_config_error), url(docsrs))]
75 CredentialsConfigError(String),
76
77 #[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 #[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 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}