use crate::client::connection::ConnectionMetadata;
use aws_smithy_types::error::metadata::{ProvideErrorMetadata, EMPTY_ERROR_METADATA};
use aws_smithy_types::error::operation::BuildError;
use aws_smithy_types::error::ErrorMetadata;
use aws_smithy_types::retry::ErrorKind;
use std::error::Error;
use std::fmt;
use std::fmt::{Debug, Display, Formatter};
type BoxError = Box<dyn Error + Send + Sync>;
pub mod builders {
use super::*;
macro_rules! source_only_error_builder {
($errorName:ident, $builderName:ident, $sourceType:ident) => {
#[doc = concat!("Builder for [`", stringify!($errorName), "`](super::", stringify!($errorName), ").")]
#[derive(Debug, Default)]
pub struct $builderName {
source: Option<$sourceType>,
}
impl $builderName {
#[doc = "Creates a new builder."]
pub fn new() -> Self { Default::default() }
#[doc = "Sets the error source."]
pub fn source(mut self, source: impl Into<$sourceType>) -> Self {
self.source = Some(source.into());
self
}
#[doc = "Sets the error source."]
pub fn set_source(&mut self, source: Option<$sourceType>) -> &mut Self {
self.source = source;
self
}
#[doc = "Builds the error context."]
pub fn build(self) -> $errorName {
$errorName { source: self.source.expect("source is required") }
}
}
};
}
source_only_error_builder!(ConstructionFailure, ConstructionFailureBuilder, BoxError);
source_only_error_builder!(TimeoutError, TimeoutErrorBuilder, BoxError);
source_only_error_builder!(DispatchFailure, DispatchFailureBuilder, ConnectorError);
#[derive(Debug)]
pub struct ResponseErrorBuilder<R> {
source: Option<BoxError>,
raw: Option<R>,
}
impl<R> Default for ResponseErrorBuilder<R> {
fn default() -> Self {
Self {
source: None,
raw: None,
}
}
}
impl<R> ResponseErrorBuilder<R> {
pub fn new() -> Self {
Default::default()
}
pub fn source(mut self, source: impl Into<BoxError>) -> Self {
self.source = Some(source.into());
self
}
pub fn set_source(&mut self, source: Option<BoxError>) -> &mut Self {
self.source = source;
self
}
pub fn raw(mut self, raw: R) -> Self {
self.raw = Some(raw);
self
}
pub fn set_raw(&mut self, raw: Option<R>) -> &mut Self {
self.raw = raw;
self
}
pub fn build(self) -> ResponseError<R> {
ResponseError {
source: self.source.expect("source is required"),
raw: self.raw.expect("a raw response is required"),
}
}
}
#[derive(Debug)]
pub struct ServiceErrorBuilder<E, R> {
source: Option<E>,
raw: Option<R>,
}
impl<E, R> Default for ServiceErrorBuilder<E, R> {
fn default() -> Self {
Self {
source: None,
raw: None,
}
}
}
impl<E, R> ServiceErrorBuilder<E, R> {
pub fn new() -> Self {
Default::default()
}
pub fn source(mut self, source: impl Into<E>) -> Self {
self.source = Some(source.into());
self
}
pub fn set_source(&mut self, source: Option<E>) -> &mut Self {
self.source = source;
self
}
pub fn raw(mut self, raw: R) -> Self {
self.raw = Some(raw);
self
}
pub fn set_raw(&mut self, raw: Option<R>) -> &mut Self {
self.raw = raw;
self
}
pub fn build(self) -> ServiceError<E, R> {
ServiceError {
source: self.source.expect("source is required"),
raw: self.raw.expect("a raw response is required"),
}
}
}
}
#[derive(Debug)]
pub struct ConstructionFailure {
pub(crate) source: BoxError,
}
impl ConstructionFailure {
pub fn builder() -> builders::ConstructionFailureBuilder {
builders::ConstructionFailureBuilder::new()
}
}
#[derive(Debug)]
pub struct TimeoutError {
source: BoxError,
}
impl TimeoutError {
pub fn builder() -> builders::TimeoutErrorBuilder {
builders::TimeoutErrorBuilder::new()
}
}
#[derive(Debug)]
pub struct DispatchFailure {
source: ConnectorError,
}
impl DispatchFailure {
pub fn builder() -> builders::DispatchFailureBuilder {
builders::DispatchFailureBuilder::new()
}
pub fn is_io(&self) -> bool {
self.source.is_io()
}
pub fn is_timeout(&self) -> bool {
self.source.is_timeout()
}
pub fn is_user(&self) -> bool {
self.source.is_user()
}
pub fn is_other(&self) -> bool {
self.source.is_other()
}
pub fn as_other(&self) -> Option<ErrorKind> {
self.source.as_other()
}
pub fn as_connector_error(&self) -> Option<&ConnectorError> {
Some(&self.source)
}
}
#[derive(Debug)]
pub struct ResponseError<R> {
source: BoxError,
raw: R,
}
impl<R> ResponseError<R> {
pub fn builder() -> builders::ResponseErrorBuilder<R> {
builders::ResponseErrorBuilder::new()
}
pub fn raw(&self) -> &R {
&self.raw
}
pub fn into_raw(self) -> R {
self.raw
}
}
#[derive(Debug)]
pub struct ServiceError<E, R> {
source: E,
raw: R,
}
impl<E, R> ServiceError<E, R> {
pub fn builder() -> builders::ServiceErrorBuilder<E, R> {
builders::ServiceErrorBuilder::new()
}
pub fn err(&self) -> &E {
&self.source
}
pub fn into_err(self) -> E {
self.source
}
pub fn raw(&self) -> &R {
&self.raw
}
pub fn into_raw(self) -> R {
self.raw
}
}
pub trait CreateUnhandledError {
fn create_unhandled_error(
source: Box<dyn Error + Send + Sync + 'static>,
meta: Option<ErrorMetadata>,
) -> Self;
}
#[non_exhaustive]
#[derive(Debug)]
pub enum SdkError<E, R> {
ConstructionFailure(ConstructionFailure),
TimeoutError(TimeoutError),
DispatchFailure(DispatchFailure),
ResponseError(ResponseError<R>),
ServiceError(ServiceError<E, R>),
}
impl<E, R> SdkError<E, R> {
pub fn construction_failure(source: impl Into<BoxError>) -> Self {
Self::ConstructionFailure(ConstructionFailure {
source: source.into(),
})
}
pub fn timeout_error(source: impl Into<BoxError>) -> Self {
Self::TimeoutError(TimeoutError {
source: source.into(),
})
}
pub fn dispatch_failure(source: ConnectorError) -> Self {
Self::DispatchFailure(DispatchFailure { source })
}
pub fn response_error(source: impl Into<BoxError>, raw: R) -> Self {
Self::ResponseError(ResponseError {
source: source.into(),
raw,
})
}
pub fn service_error(source: E, raw: R) -> Self {
Self::ServiceError(ServiceError { source, raw })
}
pub fn into_service_error(self) -> E
where
E: std::error::Error + Send + Sync + CreateUnhandledError + 'static,
R: Debug + Send + Sync + 'static,
{
match self {
Self::ServiceError(context) => context.source,
_ => E::create_unhandled_error(self.into(), None),
}
}
pub fn as_service_error(&self) -> Option<&E> {
match self {
Self::ServiceError(err) => Some(&err.source),
_ => None,
}
}
pub fn into_source(self) -> Result<Box<dyn Error + Send + Sync + 'static>, Self>
where
E: std::error::Error + Send + Sync + 'static,
{
match self {
SdkError::ConstructionFailure(context) => Ok(context.source),
SdkError::TimeoutError(context) => Ok(context.source),
SdkError::ResponseError(context) => Ok(context.source),
SdkError::DispatchFailure(context) => Ok(context.source.into()),
SdkError::ServiceError(context) => Ok(context.source.into()),
}
}
pub fn raw_response(&self) -> Option<&R> {
match self {
SdkError::ServiceError(inner) => Some(inner.raw()),
SdkError::ResponseError(inner) => Some(inner.raw()),
_ => None,
}
}
pub fn map_service_error<E2>(self, map: impl FnOnce(E) -> E2) -> SdkError<E2, R> {
match self {
SdkError::ServiceError(context) => SdkError::<E2, R>::ServiceError(ServiceError {
source: map(context.source),
raw: context.raw,
}),
SdkError::ConstructionFailure(context) => {
SdkError::<E2, R>::ConstructionFailure(context)
}
SdkError::DispatchFailure(context) => SdkError::<E2, R>::DispatchFailure(context),
SdkError::ResponseError(context) => SdkError::<E2, R>::ResponseError(context),
SdkError::TimeoutError(context) => SdkError::<E2, R>::TimeoutError(context),
}
}
}
impl<E, R> Display for SdkError<E, R> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
SdkError::ConstructionFailure(_) => write!(f, "failed to construct request"),
SdkError::TimeoutError(_) => write!(f, "request has timed out"),
SdkError::DispatchFailure(_) => write!(f, "dispatch failure"),
SdkError::ResponseError(_) => write!(f, "response error"),
SdkError::ServiceError(_) => write!(f, "service error"),
}
}
}
impl<E, R> Error for SdkError<E, R>
where
E: Error + 'static,
R: Debug,
{
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
SdkError::ConstructionFailure(context) => Some(context.source.as_ref()),
SdkError::TimeoutError(context) => Some(context.source.as_ref()),
SdkError::ResponseError(context) => Some(context.source.as_ref()),
SdkError::DispatchFailure(context) => Some(&context.source),
SdkError::ServiceError(context) => Some(&context.source),
}
}
}
impl<E, R> From<BuildError> for SdkError<E, R> {
fn from(value: BuildError) -> Self {
SdkError::ConstructionFailure(ConstructionFailure::builder().source(value).build())
}
}
impl<E, R> ProvideErrorMetadata for SdkError<E, R>
where
E: ProvideErrorMetadata,
{
fn meta(&self) -> &aws_smithy_types::error::ErrorMetadata {
match self {
SdkError::ConstructionFailure(_) => &EMPTY_ERROR_METADATA,
SdkError::TimeoutError(_) => &EMPTY_ERROR_METADATA,
SdkError::DispatchFailure(_) => &EMPTY_ERROR_METADATA,
SdkError::ResponseError(_) => &EMPTY_ERROR_METADATA,
SdkError::ServiceError(err) => err.source.meta(),
}
}
}
#[derive(Debug)]
enum ConnectorErrorKind {
Timeout,
User,
Io,
Other(Option<ErrorKind>),
}
#[derive(Debug)]
pub struct ConnectorError {
kind: ConnectorErrorKind,
source: BoxError,
connection: ConnectionStatus,
}
#[non_exhaustive]
#[derive(Debug)]
pub(crate) enum ConnectionStatus {
NeverConnected,
Unknown,
Connected(ConnectionMetadata),
}
impl Display for ConnectorError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.kind {
ConnectorErrorKind::Timeout => write!(f, "timeout"),
ConnectorErrorKind::User => write!(f, "user error"),
ConnectorErrorKind::Io => write!(f, "io error"),
ConnectorErrorKind::Other(_) => write!(f, "other"),
}
}
}
impl Error for ConnectorError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(self.source.as_ref())
}
}
impl ConnectorError {
pub fn timeout(source: BoxError) -> Self {
Self {
kind: ConnectorErrorKind::Timeout,
source,
connection: ConnectionStatus::Unknown,
}
}
pub fn with_connection(mut self, info: ConnectionMetadata) -> Self {
self.connection = ConnectionStatus::Connected(info);
self
}
pub fn never_connected(mut self) -> Self {
self.connection = ConnectionStatus::NeverConnected;
self
}
pub fn user(source: BoxError) -> Self {
Self {
kind: ConnectorErrorKind::User,
source,
connection: ConnectionStatus::Unknown,
}
}
pub fn io(source: BoxError) -> Self {
Self {
kind: ConnectorErrorKind::Io,
source,
connection: ConnectionStatus::Unknown,
}
}
pub fn other(source: BoxError, kind: Option<ErrorKind>) -> Self {
Self {
source,
kind: ConnectorErrorKind::Other(kind),
connection: ConnectionStatus::Unknown,
}
}
pub fn is_io(&self) -> bool {
matches!(self.kind, ConnectorErrorKind::Io)
}
pub fn is_timeout(&self) -> bool {
matches!(self.kind, ConnectorErrorKind::Timeout)
}
pub fn is_user(&self) -> bool {
matches!(self.kind, ConnectorErrorKind::User)
}
pub fn is_other(&self) -> bool {
matches!(self.kind, ConnectorErrorKind::Other(..))
}
pub fn as_other(&self) -> Option<ErrorKind> {
match &self.kind {
ConnectorErrorKind::Other(ek) => *ek,
_ => None,
}
}
pub fn into_source(self) -> BoxError {
self.source
}
pub fn connection_metadata(&self) -> Option<&ConnectionMetadata> {
match &self.connection {
ConnectionStatus::NeverConnected => None,
ConnectionStatus::Unknown => None,
ConnectionStatus::Connected(conn) => Some(conn),
}
}
}