egg_mode/
error.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5//! A composite error type for errors that can occur while interacting with Twitter.
6//!
7//! Any action that crosses the network to call Twitter has many places where it can go wrong.
8//! Whether it's a bad network connection, a revoked authorization token, a deleted tweet, or
9//! anything in between, those errors are all represented in the (rather sprawling) [`Error`] enum.
10//! Any errors direct from Twitter are represented as a collection of [`TwitterErrorCode`]s,
11//! contained in a [`TwitterErrors`] wrapper, and held in the `Error::TwitterError` enum variant.
12//! For more information, see the documentation for the [`Error`] enum.
13//!
14//! [`Error`]: enum.Error.html
15//! [`TwitterErrorCode`]: struct.TwitterErrorCode.html
16//! [`TwitterErrors`]: struct.TwitterErrors.html
17
18use chrono;
19use hyper;
20#[cfg(feature = "native_tls")]
21use native_tls;
22use serde::{Deserialize, Serialize};
23use serde_json;
24use std::{self, fmt};
25use tokio;
26
27use crate::common::Headers;
28
29/// Convenient alias to a Result containing a local Error type
30pub type Result<T> = std::result::Result<T, Error>;
31
32///Represents a collection of errors returned from a Twitter API call.
33///
34///This is returned as part of [`Error::TwitterError`][] whenever Twitter has rejected a call.
35///
36///[`Error::TwitterError`]: enum.Error.html
37#[derive(Debug, Deserialize, Serialize, thiserror::Error)]
38pub struct TwitterErrors {
39    /// A collection of errors
40    pub errors: Vec<TwitterErrorCode>,
41}
42
43impl fmt::Display for TwitterErrors {
44    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45        let mut first = true;
46        for e in &self.errors {
47            if first {
48                first = false;
49            } else {
50                writeln!(f, ",")?;
51            }
52
53            write!(f, "{}", e)?;
54        }
55
56        Ok(())
57    }
58}
59
60///Represents a specific error returned from a Twitter API call.
61#[derive(Debug, Deserialize, Serialize)]
62pub struct TwitterErrorCode {
63    ///The error message returned by Twitter.
64    pub message: String,
65    ///The numeric error code returned by Twitter. A list of possible error codes can be found in
66    ///the [API documentation][error-codes].
67    ///
68    ///[error-codes]: https://developer.twitter.com/en/docs/basics/response-codes
69    pub code: i32,
70}
71
72impl fmt::Display for TwitterErrorCode {
73    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
74        write!(f, "#{}: {}", self.code, self.message)
75    }
76}
77
78/// Represents an error that can occur during media processing.
79#[derive(Debug, Clone, PartialEq, Deserialize, thiserror::Error)]
80#[error("Media error {code} ({name}) - {message}")]
81pub struct MediaError {
82    /// A numeric error code assigned to the error.
83    pub code: i32,
84    /// A short name given to the error.
85    pub name: String,
86    /// The full text of the error message.
87    pub message: String,
88}
89
90/// A set of errors that can occur when interacting with Twitter.
91#[derive(Debug, thiserror::Error)]
92pub enum Error {
93    ///A URL was passed to a shortcut function that didn't match the method being called.
94    #[error("URL given did not match API method")]
95    BadUrl,
96    ///The response from Twitter was formatted incorrectly or in an unexpected manner. The enclosed
97    ///values are an explanatory string and, if applicable, the input that caused the error.
98    ///
99    ///This usually reflects a bug in this library, as it means I'm not parsing input right.
100    #[error("Invalid response received: {} ({:?})", _0, _1)]
101    InvalidResponse(&'static str, Option<String>),
102    ///The response from Twitter was missing an expected value.  The enclosed value was the
103    ///expected parameter.
104    ///
105    ///This usually reflects a bug in this library, as it means I'm expecting a value that may not
106    ///always be there, and need to update my parsing to reflect this.
107    #[error("Value missing from response: {}", _0)]
108    MissingValue(&'static str),
109    ///The `Future` being polled has already returned a completed value (or another error). In
110    ///order to retry the request, create the `Future` again.
111    #[error("Future has already completed")]
112    FutureAlreadyCompleted,
113    ///The response from Twitter returned an error structure instead of the expected response. The
114    ///enclosed value was the response from Twitter.
115    #[error("Errors returned by Twitter: {_1}")]
116    TwitterError(Headers, TwitterErrors),
117    ///The response returned from Twitter contained an error indicating that the rate limit for
118    ///that method has been reached. The enclosed value is the Unix timestamp in UTC when the next
119    ///rate-limit window will open.
120    #[error("Rate limit reached, hold until {}", _0)]
121    RateLimit(i32),
122    ///An attempt to upload a video or gif successfully uploaded the file, but failed in
123    ///post-processing. The enclosed value contains the error message from Twitter.
124    #[error("Error processing media: {}", _0)]
125    MediaError(#[from] MediaError),
126    ///The response from Twitter gave a response code that indicated an error. The enclosed value
127    ///was the response code.
128    ///
129    ///This is only returned if Twitter did not also return an [error code][TwitterErrors] in the
130    ///response body. That check is performed before examining the status code.
131    ///
132    ///[TwitterErrors]: struct.TwitterErrors.html
133    #[error("Error status received: {}", _0)]
134    BadStatus(hyper::StatusCode),
135    ///The web request experienced an error. The enclosed error was returned from hyper.
136    #[error("Network error: {}", _0)]
137    NetError(#[from] hyper::Error),
138    ///The `native_tls` implementation returned an error. The enclosed error was returned from
139    ///`native_tls`.
140    #[cfg(feature = "native_tls")]
141    #[error("TLS error: {}", _0)]
142    TlsError(#[from] native_tls::Error),
143    ///An error was experienced while processing the response stream. The enclosed error was
144    ///returned from libstd.
145    #[error("IO error: {}", _0)]
146    IOError(#[from] std::io::Error),
147    ///An error occurred while loading the JSON response. The enclosed error was returned from
148    ///`serde_json`.
149    #[error("JSON deserialize error: {}", _0)]
150    DeserializeError(#[from] serde_json::Error),
151    ///An error occurred when parsing a timestamp from Twitter. The enclosed error was returned
152    ///from chrono.
153    #[error("Error parsing timestamp: {}", _0)]
154    TimestampParseError(#[from] chrono::ParseError),
155    ///The tokio `Timer` instance was shut down while waiting on a timer, for example while waiting
156    ///for media to be processed by Twitter. The enclosed error was returned from `tokio`.
157    #[error("Timer runtime shutdown: {}", _0)]
158    TimerShutdownError(#[from] tokio::time::error::Error),
159    ///An error occurred when reading the value from a response header. The enclused error was
160    ///returned from hyper.
161    ///
162    ///This error should be considerably rare, but is included to ensure that egg-mode doesn't
163    ///panic if it receives malformed headers or the like.
164    #[error("Error decoding headers: {}", _0)]
165    HeaderParseError(#[from] hyper::header::ToStrError),
166    ///An error occurred when converting a rate-limit header to an integer. The enclosed error was
167    ///returned from the standard library.
168    ///
169    ///This error should be considerably rare, but is included to ensure that egg-mode doesn't
170    ///panic if it receives malformed headers or the like.
171    #[error("Error converting headers: {}", _0)]
172    HeaderConvertError(#[from] std::num::ParseIntError),
173}