dropbox_sdk/
error.rs

1use crate::types;
2
3/// An error occurred in the process of making an API call.
4/// This is different from the case where your call succeeded, but the operation returned an error.
5#[derive(thiserror::Error, Debug)]
6pub enum Error<E = NoError> {
7    /// An error returned by the API. Its type depends on the endpoint being called.
8    #[error("Dropbox API endpoint returned an error: {0}")]
9    Api(#[source] E),
10
11    /// Some error from the internals of the HTTP client.
12    #[error("error from HTTP client: {0}")]
13    HttpClient(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
14
15    /// Something went wrong in the process of transforming your arguments into a JSON string.
16    #[error("JSON serialization error: {0}")]
17    Json(#[from] serde_json::Error),
18
19    /// The Dropbox API response was unexpected or malformed in some way.
20    #[error("Dropbox API returned something unexpected: {0}")]
21    UnexpectedResponse(String),
22
23    /// The Dropbox API indicated that your request was malformed in some way.
24    #[error("Dropbox API indicated that the request was malformed: {0}")]
25    BadRequest(String),
26
27    /// Errors occurred during authentication.
28    #[error("Dropbox API indicated a problem with authentication: {0}")]
29    Authentication(#[source] types::auth::AuthError),
30
31    /// Your request was rejected due to rate-limiting. You can retry it later.
32    #[error("Dropbox API declined the request due to rate-limiting ({reason}), \
33        retry after {retry_after_seconds}s")]
34    RateLimited {
35        /// The server-given reason for the rate-limiting.
36        reason: types::auth::RateLimitReason,
37
38        /// You can retry this request after this many seconds.
39        retry_after_seconds: u32,
40    },
41
42    /// The user or team account doesn't have access to the endpoint or feature.
43    #[error("Dropbox API denied access to the resource: {0}")]
44    AccessDenied(#[source] types::auth::AccessError),
45
46    /// The Dropbox API server had an internal error.
47    #[error("Dropbox API had an internal server error: {0}")]
48    ServerError(String),
49
50    /// The Dropbox API returned an unexpected HTTP response code.
51    #[error("Dropbox API returned HTTP {code} - {response}")]
52    UnexpectedHttpError {
53        /// HTTP status code returned.
54        code: u16,
55
56        /// The response body.
57        response: String,
58    },
59}
60
61/// An [`Error`] without a single concrete type for the API error response, using a boxed trait
62/// object instead.
63///
64/// This is useful if a function needs to return some combination of different error types. They
65/// can be extracted later by using
66/// [`std::error::Error::downcast_ref`](https://doc.rust-lang.org/std/error/trait.Error.html#method.downcast_ref)
67/// or [`Error::downcast_ref_inner`] if desired.
68///
69/// See [`Error::boxed`] for how to convert a concretely-typed version of [`Error`] into this.
70pub type BoxedError = Error<Box<dyn std::error::Error + Send + Sync>>;
71
72impl<E: std::error::Error + 'static> Error<E> {
73    /// Look for an inner error of the given type anywhere within this error, by walking the chain
74    /// of [`std::error::Error::source`] recursively until something matches the desired type.
75    pub fn downcast_ref_inner<E2: std::error::Error + 'static>(&self) -> Option<&E2> {
76        let mut inner = Some(self as &dyn std::error::Error);
77        while let Some(e) = inner {
78            if let Some(e) = e.downcast_ref() {
79                return Some(e);
80            }
81            inner = e.source();
82        }
83        None
84    }
85}
86
87impl<E: std::error::Error + Send + Sync + 'static> Error<E> {
88    /// Change the concretely-typed API error, if any, into a boxed trait object.
89    ///
90    /// This makes it possible to combine dissimilar errors into one type, which can be broken out
91    /// later using
92    /// [`std::error::Error::downcast_ref`](https://doc.rust-lang.org/std/error/trait.Error.html#method.downcast_ref)
93    /// if desired.
94    pub fn boxed(self) -> BoxedError {
95        match self {
96            Error::Api(e) => Error::Api(Box::new(e)),
97
98            // Other variants unchanged.
99            // These have to be actually re-stated, because the (unstated) generic type of `Error`
100            // is different on the left vs the right.
101            Error::HttpClient(e) => Error::HttpClient(e),
102            Error::Json(e) => Error::Json(e),
103            Error::UnexpectedResponse(e) => Error::UnexpectedResponse(e),
104            Error::BadRequest(e) => Error::BadRequest(e),
105            Error::Authentication(e) => Error::Authentication(e),
106            Error::RateLimited { reason, retry_after_seconds } => Error::RateLimited { reason, retry_after_seconds },
107            Error::AccessDenied(e) => Error::AccessDenied(e),
108            Error::ServerError(e) => Error::ServerError(e),
109            Error::UnexpectedHttpError { code, response } => Error::UnexpectedHttpError { code, response },
110        }
111    }
112}
113
114impl Error<NoError> {
115    /// Lift an error with no possible API error value to a typed error of any type.
116    ///
117    /// Ideally this would just be `impl<E> From<Error<NoError>> for Error<E>` but that conflicts
118    /// with the reflexive conversion (E could be NoError), and Rust doesn't have negative type
119    /// bounds or specialization, so it has to be this method instead.
120    pub fn typed<E>(self) -> Error<E> {
121        match self {
122            Error::Api(x) => unreachable(x),
123            Error::HttpClient(e) => Error::HttpClient(e),
124            Error::Json(e) => Error::Json(e),
125            Error::UnexpectedResponse(e) => Error::UnexpectedResponse(e),
126            Error::BadRequest(e) => Error::BadRequest(e),
127            Error::Authentication(e) => Error::Authentication(e),
128            Error::RateLimited { reason, retry_after_seconds } => Error::RateLimited { reason, retry_after_seconds },
129            Error::AccessDenied(e) => Error::AccessDenied(e),
130            Error::ServerError(e) => Error::ServerError(e),
131            Error::UnexpectedHttpError { code, response } => Error::UnexpectedHttpError { code, response },
132        }
133    }
134}
135
136
137/// A special error type for a method that doesn't have any defined error return. You can't
138/// actually encounter a value of this type in real life; it's here to satisfy type requirements.
139#[derive(Copy, Clone)]
140pub enum NoError {}
141
142impl PartialEq<NoError> for NoError {
143    fn eq(&self, _: &NoError) -> bool {
144        unreachable(*self)
145    }
146}
147
148impl std::error::Error for NoError {
149    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
150        unreachable(*self)
151    }
152
153    fn description(&self) -> &str {
154        unreachable(*self)
155    }
156
157    fn cause(&self) -> Option<&dyn std::error::Error> {
158        unreachable(*self)
159    }
160}
161
162impl std::fmt::Debug for NoError {
163    fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        unreachable(*self)
165    }
166}
167
168impl std::fmt::Display for NoError {
169    fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170        unreachable(*self)
171    }
172}
173
174// This is the reason we can't just use the otherwise-identical `void` crate's Void type: we need
175// to implement this trait.
176impl<'de> serde::de::Deserialize<'de> for NoError {
177    fn deserialize<D: serde::de::Deserializer<'de>>(_: D)
178        -> Result<Self, D::Error>
179    {
180        Err(serde::de::Error::custom(
181            "method has no defined error type, but an error was returned"))
182    }
183}
184
185#[inline(always)]
186fn unreachable(x: NoError) -> ! {
187    match x {}
188}