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