use futures_core::TryStream;
use futures_sink::Sink;
pub(crate) use std::error::Error as StdError;
use crate::data::ResponseType;
pub use crate::data::{ApiError, EnumString, ErrorId};
pub type BoxError = Box<dyn StdError + Send + Sync>;
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(thiserror::Error, Debug)]
#[error("{kind}")]
pub struct Error {
kind: ErrorKind,
source: Option<BoxError>,
}
#[derive(thiserror::Error, displaydoc::Display, Debug, PartialEq)]
#[non_exhaustive]
pub enum ErrorKind {
Api,
TransportFull,
ConnectionRefused,
ConnectionDropped,
UnexpectedResponse,
Desynchronized,
Json,
Read,
Write,
Other,
}
#[derive(thiserror::Error, Debug, Clone, PartialEq)]
#[error("received unexpected response (expected {expected}, received {received})")]
pub struct UnexpectedResponseError {
pub(crate) expected: EnumString<ResponseType>,
pub(crate) received: EnumString<ResponseType>,
}
impl UnexpectedResponseError {
pub fn expected(&self) -> &EnumString<ResponseType> {
&self.expected
}
pub fn received(&self) -> &EnumString<ResponseType> {
&self.received
}
}
impl From<serde_json::Error> for Error {
fn from(error: serde_json::Error) -> Self {
Self::new(ErrorKind::Json).with_source(error)
}
}
impl From<ApiError> for Error {
fn from(error: ApiError) -> Self {
Self::new(ErrorKind::Api).with_source(error)
}
}
impl From<UnexpectedResponseError> for Error {
fn from(error: UnexpectedResponseError) -> Self {
Self::new(ErrorKind::UnexpectedResponse).with_source(error)
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Self::new(kind)
}
}
impl Error {
pub fn new(kind: ErrorKind) -> Self {
Self { kind, source: None }
}
pub fn to_api_error(&self) -> Option<&ApiError> {
self.find_source::<ApiError>()
}
pub fn with_source<E: Into<BoxError>>(mut self, source: E) -> Self {
self.source = Some(source.into());
self
}
pub fn into_source(self) -> Option<Box<dyn StdError + Send + Sync>> {
self.source
}
pub fn is_api_error(&self) -> bool {
self.to_api_error().is_some()
}
pub fn is_unauthenticated_error(&self) -> bool {
matches!(self.to_api_error(), Some(e) if e.is_unauthenticated())
}
pub fn from_boxed(error: BoxError) -> Self {
match error.downcast::<Self>() {
Ok(e) => *e,
Err(e) => Self::new(ErrorKind::Other).with_source(e),
}
}
pub fn kind(&self) -> &ErrorKind {
&self.kind
}
pub fn has_kind(&self, kind: ErrorKind) -> bool {
if self.kind == kind {
return true;
}
let mut source = self.source();
while let Some(e) = source {
match e.downcast_ref::<Self>() {
Some(ref found) if found.kind == kind => return true,
_ => source = e.source(),
}
}
false
}
pub fn find_source<E: StdError + 'static>(&self) -> Option<&E> {
let mut source = self.source();
while let Some(e) = source {
match e.downcast_ref::<E>() {
Some(ref found) => return Some(found),
None => source = e.source(),
}
}
None
}
}
#[doc(hidden)]
impl<T, I> From<tokio_tower::Error<T, I>> for Error
where
T: Sink<I> + TryStream,
BoxError: From<<T as Sink<I>>::Error> + From<<T as TryStream>::Error>,
{
fn from(error: tokio_tower::Error<T, I>) -> Self {
use tokio_tower::Error::*;
match error {
BrokenTransportSend(e) => Self::new(ErrorKind::Write).with_source(e),
BrokenTransportRecv(Some(e)) => Self::new(ErrorKind::Read).with_source(e),
BrokenTransportRecv(None) | ClientDropped => Self::new(ErrorKind::ConnectionDropped),
TransportFull => Self::new(ErrorKind::TransportFull),
Desynchronized => Self::new(ErrorKind::Desynchronized),
}
}
}