use super::*;
pub type Result<T> = std::result::Result<T, AsxError>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ErrorContext {
pub stage: &'static str,
pub message_id: Option<String>,
pub partner_id: Option<String>,
pub session_id: Option<String>,
}
impl ErrorContext {
#[must_use]
pub fn new(stage: &'static str) -> Self {
Self {
stage,
message_id: None,
partner_id: None,
session_id: None,
}
}
#[must_use]
pub fn for_session(stage: &'static str, session: &SessionContext) -> Self {
Self::new(stage).with_session_and_partner(session.session_id(), session.partner_id())
}
#[must_use]
pub fn for_session_with_message(
stage: &'static str,
session: &SessionContext,
message_id: impl Into<String>,
) -> Self {
Self::new(stage)
.with_session_and_partner(session.session_id(), session.partner_id())
.with_message_id(message_id)
}
#[must_use]
pub fn with_session_and_partner(
mut self,
session_id: impl Into<String>,
partner_id: impl Into<String>,
) -> Self {
self.session_id = Some(session_id.into());
self.partner_id = Some(partner_id.into());
self
}
#[must_use]
pub fn with_message_id(mut self, message_id: impl Into<String>) -> Self {
self.message_id = Some(message_id.into());
self
}
#[must_use]
pub fn with_partner_id(mut self, partner_id: impl Into<String>) -> Self {
self.partner_id = Some(partner_id.into());
self
}
#[must_use]
pub fn with_session_id(mut self, session_id: impl Into<String>) -> Self {
self.session_id = Some(session_id.into());
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorCode {
InvalidInput,
ParseFailed,
SecurityVerificationFailed,
DecryptionFailed,
PolicyViolation,
TransportFailure,
InteropViolation,
ReliabilityFailure,
NotFound,
CapacityExhausted,
PayloadTooLarge,
StorageBackendFailure,
CertificateRevoked,
CertificateExpired,
Timeout,
}
impl ErrorCode {
pub fn as_str(self) -> &'static str {
match self {
Self::InvalidInput => "invalid_input",
Self::ParseFailed => "parse_failed",
Self::SecurityVerificationFailed => "security_verification_failed",
Self::DecryptionFailed => "decryption_failed",
Self::PolicyViolation => "policy_violation",
Self::TransportFailure => "transport_failure",
Self::InteropViolation => "interop_violation",
Self::ReliabilityFailure => "reliability_failure",
Self::NotFound => "not_found",
Self::CapacityExhausted => "capacity_exhausted",
Self::PayloadTooLarge => "payload_too_large",
Self::StorageBackendFailure => "storage_backend_failure",
Self::CertificateRevoked => "certificate_revoked",
Self::CertificateExpired => "certificate_expired",
Self::Timeout => "timeout",
}
}
pub fn to_http_status(self) -> u16 {
match self {
Self::InvalidInput => 400,
Self::ParseFailed => 400,
Self::SecurityVerificationFailed => 401,
Self::DecryptionFailed => 400,
Self::PolicyViolation => 422,
Self::TransportFailure => 502,
Self::InteropViolation => 400,
Self::ReliabilityFailure => 503,
Self::NotFound => 404,
Self::CapacityExhausted => 429,
Self::PayloadTooLarge => 413,
Self::StorageBackendFailure => 503,
Self::CertificateRevoked => 403,
Self::CertificateExpired => 403,
Self::Timeout => 504,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AsxError {
pub code: ErrorCode,
pub message: String,
pub context: Box<ErrorContext>,
}
impl AsxError {
pub fn new(code: ErrorCode, message: impl Into<String>, context: ErrorContext) -> Self {
Self {
code,
message: message.into(),
context: Box::new(context),
}
}
#[must_use]
pub fn with_partner_id(mut self, partner_id: impl Into<String>) -> Self {
self.context = Box::new(self.context.with_partner_id(partner_id));
self
}
#[must_use]
pub fn with_session_id(mut self, session_id: impl Into<String>) -> Self {
self.context = Box::new(self.context.with_session_id(session_id));
self
}
#[must_use]
pub fn with_session_and_partner(
mut self,
session_id: impl Into<String>,
partner_id: impl Into<String>,
) -> Self {
self.context = Box::new(
self.context
.with_session_and_partner(session_id, partner_id),
);
self
}
#[must_use]
pub fn with_message_id(mut self, message_id: impl Into<String>) -> Self {
self.context = Box::new(self.context.with_message_id(message_id));
self
}
#[inline]
pub fn is_duplicate(&self) -> bool {
self.code == ErrorCode::ReliabilityFailure && self.message.contains("replay")
}
#[inline]
pub fn is_storage_failure(&self) -> bool {
self.code == ErrorCode::StorageBackendFailure
}
}
impl fmt::Display for AsxError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} [{}|stage={}]",
self.message,
self.code.as_str(),
self.context.stage
)?;
if let Some(pid) = &self.context.partner_id {
write!(f, "[partner={pid}]")?;
}
if let Some(mid) = &self.context.message_id {
write!(f, "[msg={mid}]")?;
}
if let Some(sid) = &self.context.session_id {
write!(f, "[session={sid}]")?;
}
Ok(())
}
}
impl std::error::Error for AsxError {}