Skip to main content

rustack_iam_model/
error.rs

1//! IAM error types for the awsQuery XML protocol.
2//!
3//! IAM errors use XML format with `<Error>` elements containing
4//! `<Type>`, `<Code>`, and `<Message>` fields.
5
6use std::fmt;
7
8/// Well-known IAM error codes.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
10#[non_exhaustive]
11pub enum IamErrorCode {
12    /// ConcurrentModificationException error.
13    #[default]
14    ConcurrentModificationException,
15    /// DeleteConflict error.
16    DeleteConflict,
17    /// DeleteConflictException error.
18    DeleteConflictException,
19    /// EntityAlreadyExists error.
20    EntityAlreadyExists,
21    /// EntityAlreadyExistsException error.
22    EntityAlreadyExistsException,
23    /// EntityTemporarilyUnmodifiableException error.
24    EntityTemporarilyUnmodifiableException,
25    /// InvalidAction error.
26    InvalidAction,
27    /// InvalidInput error.
28    InvalidInput,
29    /// InvalidInputException error.
30    InvalidInputException,
31    /// LimitExceeded error.
32    LimitExceeded,
33    /// LimitExceededException error.
34    LimitExceededException,
35    /// MalformedPolicyDocument error.
36    MalformedPolicyDocument,
37    /// MalformedPolicyDocumentException error.
38    MalformedPolicyDocumentException,
39    /// MissingAction error.
40    MissingAction,
41    /// NoSuchEntity error.
42    NoSuchEntity,
43    /// NoSuchEntityException error.
44    NoSuchEntityException,
45    /// PolicyEvaluationException error.
46    PolicyEvaluationException,
47    /// PolicyNotAttachableException error.
48    PolicyNotAttachableException,
49    /// ServiceFailure error.
50    ServiceFailure,
51    /// ServiceFailureException error.
52    ServiceFailureException,
53    /// UnmodifiableEntityException error.
54    UnmodifiableEntityException,
55}
56
57impl IamErrorCode {
58    /// Returns the short error type string for the JSON `__type` field.
59    #[must_use]
60    pub fn error_type(&self) -> &'static str {
61        self.as_str()
62    }
63
64    /// Returns the short error code string.
65    #[must_use]
66    pub fn as_str(&self) -> &'static str {
67        match self {
68            Self::ConcurrentModificationException => "ConcurrentModificationException",
69            Self::DeleteConflict => "DeleteConflict",
70            Self::DeleteConflictException => "DeleteConflictException",
71            Self::EntityAlreadyExists => "EntityAlreadyExists",
72            Self::EntityAlreadyExistsException => "EntityAlreadyExistsException",
73            Self::EntityTemporarilyUnmodifiableException => {
74                "EntityTemporarilyUnmodifiableException"
75            }
76            Self::InvalidAction => "InvalidAction",
77            Self::InvalidInput => "InvalidInput",
78            Self::InvalidInputException => "InvalidInputException",
79            Self::LimitExceeded => "LimitExceeded",
80            Self::LimitExceededException => "LimitExceededException",
81            Self::MalformedPolicyDocument => "MalformedPolicyDocument",
82            Self::MalformedPolicyDocumentException => "MalformedPolicyDocumentException",
83            Self::MissingAction => "MissingAction",
84            Self::NoSuchEntity => "NoSuchEntity",
85            Self::NoSuchEntityException => "NoSuchEntityException",
86            Self::PolicyEvaluationException => "PolicyEvaluationException",
87            Self::PolicyNotAttachableException => "PolicyNotAttachableException",
88            Self::ServiceFailure => "ServiceFailure",
89            Self::ServiceFailureException => "ServiceFailureException",
90            Self::UnmodifiableEntityException => "UnmodifiableEntityException",
91        }
92    }
93
94    /// Returns the error code string for the XML `<Code>` element.
95    #[must_use]
96    pub fn code(&self) -> &'static str {
97        self.as_str()
98    }
99
100    /// Returns whether this is a `Sender` or `Receiver` fault for XML `<Type>`.
101    #[must_use]
102    pub fn fault(&self) -> &'static str {
103        match self {
104            Self::ServiceFailure
105            | Self::ServiceFailureException
106            | Self::PolicyEvaluationException => "Receiver",
107            _ => "Sender",
108        }
109    }
110
111    /// HTTP status code for this error.
112    #[must_use]
113    pub fn status_code(&self) -> http::StatusCode {
114        self.default_status_code()
115    }
116
117    /// Returns the default HTTP status code for this error.
118    #[must_use]
119    pub fn default_status_code(&self) -> http::StatusCode {
120        match self {
121            Self::InvalidAction
122            | Self::InvalidInput
123            | Self::InvalidInputException
124            | Self::MalformedPolicyDocument
125            | Self::MalformedPolicyDocumentException
126            | Self::MissingAction
127            | Self::PolicyNotAttachableException
128            | Self::UnmodifiableEntityException => http::StatusCode::BAD_REQUEST,
129            Self::NoSuchEntity | Self::NoSuchEntityException => http::StatusCode::NOT_FOUND,
130            Self::ConcurrentModificationException
131            | Self::DeleteConflict
132            | Self::DeleteConflictException
133            | Self::EntityAlreadyExists
134            | Self::EntityAlreadyExistsException
135            | Self::EntityTemporarilyUnmodifiableException
136            | Self::LimitExceeded
137            | Self::LimitExceededException => http::StatusCode::CONFLICT,
138            Self::PolicyEvaluationException
139            | Self::ServiceFailure
140            | Self::ServiceFailureException => http::StatusCode::INTERNAL_SERVER_ERROR,
141        }
142    }
143}
144
145impl fmt::Display for IamErrorCode {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        f.write_str(self.as_str())
148    }
149}
150
151/// An IAM error response.
152#[derive(Debug)]
153pub struct IamError {
154    /// The error code.
155    pub code: IamErrorCode,
156    /// A human-readable error message.
157    pub message: String,
158    /// The HTTP status code.
159    pub status_code: http::StatusCode,
160    /// The underlying source error, if any.
161    pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
162}
163
164impl fmt::Display for IamError {
165    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        write!(f, "IamError({}): {}", self.code, self.message)
167    }
168}
169
170impl std::error::Error for IamError {
171    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
172        self.source
173            .as_ref()
174            .map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
175    }
176}
177
178impl IamError {
179    /// Create a new `IamError` from an error code.
180    #[must_use]
181    pub fn new(code: IamErrorCode) -> Self {
182        Self {
183            status_code: code.default_status_code(),
184            message: code.as_str().to_owned(),
185            code,
186            source: None,
187        }
188    }
189
190    /// Create a new `IamError` with a custom message.
191    #[must_use]
192    pub fn with_message(code: IamErrorCode, message: impl Into<String>) -> Self {
193        Self {
194            status_code: code.default_status_code(),
195            message: message.into(),
196            code,
197            source: None,
198        }
199    }
200
201    /// Returns the `__type` string for the JSON error response.
202    #[must_use]
203    pub fn error_type(&self) -> &'static str {
204        self.code.error_type()
205    }
206
207    /// Internal error.
208    #[must_use]
209    pub fn internal_error(message: impl Into<String>) -> Self {
210        Self::with_message(IamErrorCode::ServiceFailure, message)
211    }
212
213    /// Missing action parameter.
214    #[must_use]
215    pub fn missing_action() -> Self {
216        Self::with_message(IamErrorCode::MissingAction, "Missing Action parameter")
217    }
218
219    /// Unknown operation.
220    #[must_use]
221    pub fn unknown_operation(action: &str) -> Self {
222        Self::with_message(
223            IamErrorCode::InvalidAction,
224            format!("The action {action} is not valid for this endpoint."),
225        )
226    }
227
228    /// Not implemented.
229    #[must_use]
230    pub fn not_implemented(operation: &str) -> Self {
231        Self::with_message(
232            IamErrorCode::ServiceFailure,
233            format!("Operation {operation} is not yet implemented"),
234        )
235    }
236
237    /// Entity not found.
238    #[must_use]
239    pub fn no_such_entity(message: impl Into<String>) -> Self {
240        Self::with_message(IamErrorCode::NoSuchEntity, message)
241    }
242
243    /// Entity already exists.
244    #[must_use]
245    pub fn entity_already_exists(message: impl Into<String>) -> Self {
246        Self::with_message(IamErrorCode::EntityAlreadyExists, message)
247    }
248
249    /// Delete conflict (entity has subordinate entities).
250    #[must_use]
251    pub fn delete_conflict(message: impl Into<String>) -> Self {
252        Self::with_message(IamErrorCode::DeleteConflict, message)
253    }
254
255    /// Limit exceeded.
256    #[must_use]
257    pub fn limit_exceeded(message: impl Into<String>) -> Self {
258        Self::with_message(IamErrorCode::LimitExceeded, message)
259    }
260
261    /// Malformed policy document.
262    #[must_use]
263    pub fn malformed_policy_document(message: impl Into<String>) -> Self {
264        Self::with_message(IamErrorCode::MalformedPolicyDocument, message)
265    }
266
267    /// Invalid input.
268    #[must_use]
269    pub fn invalid_input(message: impl Into<String>) -> Self {
270        Self::with_message(IamErrorCode::InvalidInput, message)
271    }
272
273    /// Invalid security / auth error.
274    #[must_use]
275    pub fn invalid_security(message: impl Into<String>) -> Self {
276        Self::with_message(IamErrorCode::InvalidInput, message)
277    }
278}
279
280/// Create an `IamError` from an error code.
281///
282/// # Examples
283///
284/// ```ignore
285/// let err = iam_error!(ConcurrentModificationException);
286/// assert_eq!(err.code, IamErrorCode::ConcurrentModificationException);
287/// ```
288#[macro_export]
289macro_rules! iam_error {
290    ($code:ident) => {
291        $crate::error::IamError::new($crate::error::IamErrorCode::$code)
292    };
293    ($code:ident, $msg:expr) => {
294        $crate::error::IamError::with_message($crate::error::IamErrorCode::$code, $msg)
295    };
296}