use std::error::Error as StdError;
use crate::http::HttpStatusError;
use crate::request::IqError as ClientIqError;
use wacore::request::{IqError as CoreIqError, ServerErrorCode};
use wacore::store::error::StoreError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct ServerRejection<'a> {
pub code: u16,
pub text: &'a str,
pub error_type: Option<&'a str>,
pub backoff: Option<u32>,
}
#[derive(Clone)]
pub struct Sources<'a> {
next: Option<&'a (dyn StdError + 'static)>,
}
impl<'a> Iterator for Sources<'a> {
type Item = &'a (dyn StdError + 'static);
fn next(&mut self) -> Option<Self::Item> {
let current = self.next?;
self.next = current.source();
Some(current)
}
}
pub trait ErrorChainExt {
#[doc(hidden)]
fn as_dyn_error(&self) -> &(dyn StdError + 'static);
fn sources(&self) -> Sources<'_> {
Sources {
next: Some(self.as_dyn_error()),
}
}
fn server_rejection(&self) -> Option<ServerRejection<'_>> {
self.sources().find_map(server_rejection_of)
}
fn http_status(&self) -> Option<u16> {
self.sources()
.find_map(|cause| cause.downcast_ref::<HttpStatusError>())
.map(|refused| refused.status)
}
fn is_timeout(&self) -> bool {
self.sources().any(|cause| {
if let Some(iq) = cause.downcast_ref::<ClientIqError>() {
return iq.is_timeout();
}
if let Some(iq) = cause.downcast_ref::<CoreIqError>() {
return iq.is_timeout();
}
if let Some(connect) = cause.downcast_ref::<crate::client::ConnectError>() {
return connect.is_timeout();
}
cause
.downcast_ref::<crate::handshake::HandshakeError>()
.is_some_and(crate::handshake::HandshakeError::is_timeout)
})
}
fn is_transport_unavailable(&self) -> bool {
self.sources().any(|cause| {
if let Some(client) = cause.downcast_ref::<crate::client::ClientError>() {
return client.is_transport_unavailable();
}
if let Some(iq) = cause.downcast_ref::<ClientIqError>() {
return iq.is_transport_unavailable();
}
if let Some(encrypt) = cause.downcast_ref::<crate::socket::error::EncryptSendError>() {
return encrypt.is_transport_unavailable();
}
cause
.downcast_ref::<CoreIqError>()
.is_some_and(CoreIqError::is_transport_unavailable)
})
}
fn store_failure(&self) -> Option<&StoreError> {
self.sources().find_map(|cause| cause.downcast_ref())
}
}
fn server_rejection_of<'a>(cause: &'a (dyn StdError + 'static)) -> Option<ServerRejection<'a>> {
if let Some(CoreIqError::ServerError {
code,
text,
error_type,
backoff,
}) = cause.downcast_ref::<CoreIqError>()
{
return Some(ServerRejection {
code: *code,
text,
error_type: error_type.as_deref(),
backoff: *backoff,
});
}
if let Some(ClientIqError::ServerError {
code,
text,
error_type,
backoff,
}) = cause.downcast_ref::<ClientIqError>()
{
return Some(ServerRejection {
code: *code,
text,
error_type: error_type.as_deref(),
backoff: *backoff,
});
}
let shared = cause.downcast_ref::<ServerErrorCode>()?;
Some(ServerRejection {
code: shared.code,
text: &shared.text,
error_type: shared.error_type.as_deref(),
backoff: shared.backoff,
})
}
impl<E: StdError + 'static> ErrorChainExt for E {
fn as_dyn_error(&self) -> &(dyn StdError + 'static) {
self
}
}
impl ErrorChainExt for dyn StdError + 'static {
fn as_dyn_error(&self) -> &(dyn StdError + 'static) {
self
}
}
impl ErrorChainExt for dyn StdError + Send + Sync + 'static {
fn as_dyn_error(&self) -> &(dyn StdError + 'static) {
self
}
}