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}