2023-02-01 12:05:18 +00:00
|
|
|
use std::string::FromUtf8Error;
|
2022-12-05 13:52:48 +00:00
|
|
|
use std::{error, fmt, io::Error as IoError, num::TryFromIntError};
|
2022-11-27 14:44:43 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "env")]
|
|
|
|
use envy::Error as EnvyError;
|
2023-02-01 20:06:37 +00:00
|
|
|
use is_variant::IsVariant;
|
2022-11-27 14:44:43 +00:00
|
|
|
use reqwest::{header::ToStrError as HeaderStrError, Error as HttpError, StatusCode};
|
2022-11-29 23:50:29 +00:00
|
|
|
use serde::Deserialize;
|
2022-11-27 14:44:43 +00:00
|
|
|
use serde_json::Error as SerdeError;
|
|
|
|
use serde_urlencoded::ser::Error as UrlEncodedError;
|
|
|
|
#[cfg(feature = "toml")]
|
|
|
|
use tomlcrate::de::Error as TomlDeError;
|
|
|
|
#[cfg(feature = "toml")]
|
|
|
|
use tomlcrate::ser::Error as TomlSerError;
|
2022-11-29 23:50:29 +00:00
|
|
|
use url::ParseError as UrlError;
|
2022-11-27 14:44:43 +00:00
|
|
|
|
|
|
|
/// Convience type over `std::result::Result` with `Error` as the error type.
|
|
|
|
pub type Result<T> = ::std::result::Result<T, Error>;
|
|
|
|
|
|
|
|
/// enum of possible errors encountered using the mastodon API.
|
2023-02-01 20:06:37 +00:00
|
|
|
#[derive(Debug, thiserror::Error, IsVariant)]
|
2022-11-27 14:44:43 +00:00
|
|
|
pub enum Error {
|
|
|
|
/// Error from the Mastodon API. This typically means something went
|
|
|
|
/// wrong with your authentication or data.
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("API error: status: {status:?}, response:\n{response:#?}")]
|
2022-12-23 15:09:33 +00:00
|
|
|
Api {
|
|
|
|
/// The response status.
|
|
|
|
status: StatusCode,
|
|
|
|
/// The JSON-decoded error response from the server.
|
|
|
|
response: ApiError,
|
|
|
|
},
|
2022-11-27 14:44:43 +00:00
|
|
|
/// Error deserialising to json. Typically represents a breaking change in
|
|
|
|
/// the Mastodon API
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("error from serde")]
|
|
|
|
Serde(#[from] SerdeError),
|
2022-11-27 14:44:43 +00:00
|
|
|
/// Error serializing to url-encoded string
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("error serializing to url-encoded string")]
|
|
|
|
UrlEncoded(#[from] UrlEncodedError),
|
2022-11-27 14:44:43 +00:00
|
|
|
/// Error encountered in the HTTP backend while requesting a route.
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("Error encountered in the HTTP backend while requesting a route.")]
|
|
|
|
Http(#[from] HttpError),
|
2022-11-27 14:44:43 +00:00
|
|
|
/// Wrapper around the `std::io::Error` struct.
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("io error")]
|
|
|
|
Io(#[from] IoError),
|
2022-11-27 14:44:43 +00:00
|
|
|
/// Wrapper around the `url::ParseError` struct.
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("error parsing URL")]
|
|
|
|
Url(#[from] UrlError),
|
2022-11-27 14:44:43 +00:00
|
|
|
/// Missing Client Id.
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("Missing Client Id.")]
|
2022-11-27 14:44:43 +00:00
|
|
|
ClientIdRequired,
|
|
|
|
/// Missing Client Secret.
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("Missing Client Secret.")]
|
2022-11-27 14:44:43 +00:00
|
|
|
ClientSecretRequired,
|
|
|
|
/// Missing Access Token.
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("Missing Access Token.")]
|
2022-11-27 14:44:43 +00:00
|
|
|
AccessTokenRequired,
|
|
|
|
/// MastodonBuilder & AppBuilder error
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("builder required field {0:?} to be constructed")]
|
2022-11-27 14:44:43 +00:00
|
|
|
MissingField(&'static str),
|
|
|
|
#[cfg(feature = "toml")]
|
|
|
|
/// Error serializing to toml
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("Error serializing to toml")]
|
|
|
|
TomlSer(#[from] TomlSerError),
|
2022-11-27 14:44:43 +00:00
|
|
|
#[cfg(feature = "toml")]
|
|
|
|
/// Error deserializing from toml
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("Error deserializing from toml")]
|
|
|
|
TomlDe(#[from] TomlDeError),
|
2022-12-29 17:41:42 +00:00
|
|
|
|
|
|
|
#[cfg(any(feature = "toml", feature = "json"))]
|
|
|
|
/// Error raised in the helpers::json::to_writer or helpers::toml::to_writer function if not
|
|
|
|
/// all bytes were written to the writer
|
|
|
|
#[error("Not all bytes were written")]
|
|
|
|
NotAllBytesWritten,
|
|
|
|
|
2022-11-27 14:44:43 +00:00
|
|
|
/// Error converting an http header to a string
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("Error converting an http header to a string")]
|
|
|
|
HeaderStrError(#[from] HeaderStrError),
|
2022-11-27 14:44:43 +00:00
|
|
|
/// Error parsing the http Link header
|
2022-12-29 18:00:50 +00:00
|
|
|
#[error("error parsing http link header")]
|
|
|
|
LinkHeaderParse(#[from] parse_link_header::Error),
|
|
|
|
/// Error returned when an unexpected rel was parsed.
|
|
|
|
#[error("unrecognized rel {rel:?} in link header {link:?}")]
|
|
|
|
UnrecognizedRel {
|
|
|
|
/// The relation which was not recognized
|
|
|
|
rel: String,
|
|
|
|
/// The raw link header
|
|
|
|
link: String,
|
|
|
|
},
|
2022-11-27 14:44:43 +00:00
|
|
|
#[cfg(feature = "env")]
|
2022-12-29 18:18:04 +00:00
|
|
|
/// Error deserializing config from the environment
|
|
|
|
#[error("Error deserializing config from the environment")]
|
|
|
|
Envy(#[from] EnvyError),
|
2022-12-05 13:52:48 +00:00
|
|
|
/// An integer conversion was attempted, but the value didn't fit into the
|
|
|
|
/// target type.
|
|
|
|
///
|
|
|
|
/// At the time of writing, this can only be triggered when a file is
|
|
|
|
/// larger than the system's usize allows.
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("integer didn't fit in the target size")]
|
|
|
|
IntConversion(#[from] TryFromIntError),
|
2022-12-30 09:41:37 +00:00
|
|
|
/// Error from mastodon-async-entities
|
|
|
|
#[error(transparent)]
|
|
|
|
Entities(#[from] mastodon_async_entities::error::Error),
|
2023-02-01 12:05:18 +00:00
|
|
|
/// Error parsing UTF-8 string from bytes
|
|
|
|
#[error(transparent)]
|
|
|
|
FromUtf8(#[from] FromUtf8Error),
|
2022-11-27 14:44:43 +00:00
|
|
|
/// Other errors
|
2022-12-29 18:18:04 +00:00
|
|
|
#[error("other error: {0:?}")]
|
2022-11-27 14:44:43 +00:00
|
|
|
Other(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Error returned from the Mastodon API.
|
2022-12-23 15:09:33 +00:00
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
2022-11-27 14:44:43 +00:00
|
|
|
pub struct ApiError {
|
2022-12-23 15:09:33 +00:00
|
|
|
/// The error message.
|
|
|
|
pub error: String,
|
|
|
|
/// A longer description of the error, mainly provided with the OAuth API.
|
2022-11-27 14:44:43 +00:00
|
|
|
pub error_description: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for ApiError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "{:?}", self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl error::Error for ApiError {}
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
/// Used to easily create errors from strings
|
|
|
|
macro_rules! format_err {
|
|
|
|
( $( $arg:tt )* ) => {
|
|
|
|
{
|
2022-12-27 14:56:19 +00:00
|
|
|
$crate::Error::Other(format!($($arg)*))
|
2022-11-27 14:44:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use std::io;
|
|
|
|
|
|
|
|
macro_rules! assert_is {
|
|
|
|
($err:ident, $variant:pat) => {
|
|
|
|
assert!(match $err {
|
|
|
|
$variant => true,
|
|
|
|
_ => false,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-12-05 13:52:48 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn from_http_error() {
|
|
|
|
let err: HttpError = reqwest::get("not an actual URL").await.unwrap_err();
|
2022-11-27 14:44:43 +00:00
|
|
|
let err: Error = Error::from(err);
|
|
|
|
assert_is!(err, Error::Http(..));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn from_io_error() {
|
|
|
|
let err: IoError = io::Error::new(io::ErrorKind::Other, "other error");
|
|
|
|
let err: Error = Error::from(err);
|
|
|
|
assert_is!(err, Error::Io(..));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn from_serde_error() {
|
|
|
|
let err: SerdeError = serde_json::from_str::<()>("not valid json").unwrap_err();
|
|
|
|
let err: Error = Error::from(err);
|
|
|
|
assert_is!(err, Error::Serde(..));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn from_url_encoded_error() {
|
|
|
|
let err: UrlEncodedError = serde_urlencoded::ser::Error::Custom("error".into());
|
|
|
|
let err: Error = Error::from(err);
|
|
|
|
assert_is!(err, Error::UrlEncoded(..));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn from_url_error() {
|
|
|
|
let err: UrlError = UrlError::EmptyHost;
|
|
|
|
let err: Error = Error::from(err);
|
|
|
|
assert_is!(err, Error::Url(..));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "toml")]
|
|
|
|
#[test]
|
|
|
|
fn from_toml_de_error() {
|
|
|
|
use tomlcrate;
|
|
|
|
let err: TomlDeError = tomlcrate::from_str::<()>("not valid toml").unwrap_err();
|
|
|
|
let err: Error = Error::from(err);
|
|
|
|
assert_is!(err, Error::TomlDe(..));
|
|
|
|
}
|
|
|
|
}
|