use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum IamErrorCode {
#[default]
ConcurrentModificationException,
DeleteConflict,
DeleteConflictException,
EntityAlreadyExists,
EntityAlreadyExistsException,
EntityTemporarilyUnmodifiableException,
InvalidAction,
InvalidInput,
InvalidInputException,
LimitExceeded,
LimitExceededException,
MalformedPolicyDocument,
MalformedPolicyDocumentException,
MissingAction,
NoSuchEntity,
NoSuchEntityException,
PolicyEvaluationException,
PolicyNotAttachableException,
ServiceFailure,
ServiceFailureException,
UnmodifiableEntityException,
}
impl IamErrorCode {
#[must_use]
pub fn error_type(&self) -> &'static str {
self.as_str()
}
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::ConcurrentModificationException => "ConcurrentModificationException",
Self::DeleteConflict => "DeleteConflict",
Self::DeleteConflictException => "DeleteConflictException",
Self::EntityAlreadyExists => "EntityAlreadyExists",
Self::EntityAlreadyExistsException => "EntityAlreadyExistsException",
Self::EntityTemporarilyUnmodifiableException => {
"EntityTemporarilyUnmodifiableException"
}
Self::InvalidAction => "InvalidAction",
Self::InvalidInput => "InvalidInput",
Self::InvalidInputException => "InvalidInputException",
Self::LimitExceeded => "LimitExceeded",
Self::LimitExceededException => "LimitExceededException",
Self::MalformedPolicyDocument => "MalformedPolicyDocument",
Self::MalformedPolicyDocumentException => "MalformedPolicyDocumentException",
Self::MissingAction => "MissingAction",
Self::NoSuchEntity => "NoSuchEntity",
Self::NoSuchEntityException => "NoSuchEntityException",
Self::PolicyEvaluationException => "PolicyEvaluationException",
Self::PolicyNotAttachableException => "PolicyNotAttachableException",
Self::ServiceFailure => "ServiceFailure",
Self::ServiceFailureException => "ServiceFailureException",
Self::UnmodifiableEntityException => "UnmodifiableEntityException",
}
}
#[must_use]
pub fn code(&self) -> &'static str {
self.as_str()
}
#[must_use]
pub fn fault(&self) -> &'static str {
match self {
Self::ServiceFailure
| Self::ServiceFailureException
| Self::PolicyEvaluationException => "Receiver",
_ => "Sender",
}
}
#[must_use]
pub fn status_code(&self) -> http::StatusCode {
self.default_status_code()
}
#[must_use]
pub fn default_status_code(&self) -> http::StatusCode {
match self {
Self::InvalidAction
| Self::InvalidInput
| Self::InvalidInputException
| Self::MalformedPolicyDocument
| Self::MalformedPolicyDocumentException
| Self::MissingAction
| Self::PolicyNotAttachableException
| Self::UnmodifiableEntityException => http::StatusCode::BAD_REQUEST,
Self::NoSuchEntity | Self::NoSuchEntityException => http::StatusCode::NOT_FOUND,
Self::ConcurrentModificationException
| Self::DeleteConflict
| Self::DeleteConflictException
| Self::EntityAlreadyExists
| Self::EntityAlreadyExistsException
| Self::EntityTemporarilyUnmodifiableException
| Self::LimitExceeded
| Self::LimitExceededException => http::StatusCode::CONFLICT,
Self::PolicyEvaluationException
| Self::ServiceFailure
| Self::ServiceFailureException => http::StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
impl fmt::Display for IamErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug)]
pub struct IamError {
pub code: IamErrorCode,
pub message: String,
pub status_code: http::StatusCode,
pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
}
impl fmt::Display for IamError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "IamError({}): {}", self.code, self.message)
}
}
impl std::error::Error for IamError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source
.as_ref()
.map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
}
}
impl IamError {
#[must_use]
pub fn new(code: IamErrorCode) -> Self {
Self {
status_code: code.default_status_code(),
message: code.as_str().to_owned(),
code,
source: None,
}
}
#[must_use]
pub fn with_message(code: IamErrorCode, message: impl Into<String>) -> Self {
Self {
status_code: code.default_status_code(),
message: message.into(),
code,
source: None,
}
}
#[must_use]
pub fn error_type(&self) -> &'static str {
self.code.error_type()
}
#[must_use]
pub fn internal_error(message: impl Into<String>) -> Self {
Self::with_message(IamErrorCode::ServiceFailure, message)
}
#[must_use]
pub fn missing_action() -> Self {
Self::with_message(IamErrorCode::MissingAction, "Missing Action parameter")
}
#[must_use]
pub fn unknown_operation(action: &str) -> Self {
Self::with_message(
IamErrorCode::InvalidAction,
format!("The action {action} is not valid for this endpoint."),
)
}
#[must_use]
pub fn not_implemented(operation: &str) -> Self {
Self::with_message(
IamErrorCode::ServiceFailure,
format!("Operation {operation} is not yet implemented"),
)
}
#[must_use]
pub fn no_such_entity(message: impl Into<String>) -> Self {
Self::with_message(IamErrorCode::NoSuchEntity, message)
}
#[must_use]
pub fn entity_already_exists(message: impl Into<String>) -> Self {
Self::with_message(IamErrorCode::EntityAlreadyExists, message)
}
#[must_use]
pub fn delete_conflict(message: impl Into<String>) -> Self {
Self::with_message(IamErrorCode::DeleteConflict, message)
}
#[must_use]
pub fn limit_exceeded(message: impl Into<String>) -> Self {
Self::with_message(IamErrorCode::LimitExceeded, message)
}
#[must_use]
pub fn malformed_policy_document(message: impl Into<String>) -> Self {
Self::with_message(IamErrorCode::MalformedPolicyDocument, message)
}
#[must_use]
pub fn invalid_input(message: impl Into<String>) -> Self {
Self::with_message(IamErrorCode::InvalidInput, message)
}
#[must_use]
pub fn invalid_security(message: impl Into<String>) -> Self {
Self::with_message(IamErrorCode::InvalidInput, message)
}
}
#[macro_export]
macro_rules! iam_error {
($code:ident) => {
$crate::error::IamError::new($crate::error::IamErrorCode::$code)
};
($code:ident, $msg:expr) => {
$crate::error::IamError::with_message($crate::error::IamErrorCode::$code, $msg)
};
}