1use std::fmt;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
10#[non_exhaustive]
11pub enum IamErrorCode {
12 #[default]
14 ConcurrentModificationException,
15 DeleteConflict,
17 DeleteConflictException,
19 EntityAlreadyExists,
21 EntityAlreadyExistsException,
23 EntityTemporarilyUnmodifiableException,
25 InvalidAction,
27 InvalidInput,
29 InvalidInputException,
31 LimitExceeded,
33 LimitExceededException,
35 MalformedPolicyDocument,
37 MalformedPolicyDocumentException,
39 MissingAction,
41 NoSuchEntity,
43 NoSuchEntityException,
45 PolicyEvaluationException,
47 PolicyNotAttachableException,
49 ServiceFailure,
51 ServiceFailureException,
53 UnmodifiableEntityException,
55}
56
57impl IamErrorCode {
58 #[must_use]
60 pub fn error_type(&self) -> &'static str {
61 self.as_str()
62 }
63
64 #[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 #[must_use]
96 pub fn code(&self) -> &'static str {
97 self.as_str()
98 }
99
100 #[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 #[must_use]
113 pub fn status_code(&self) -> http::StatusCode {
114 self.default_status_code()
115 }
116
117 #[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#[derive(Debug)]
153pub struct IamError {
154 pub code: IamErrorCode,
156 pub message: String,
158 pub status_code: http::StatusCode,
160 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 #[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 #[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 #[must_use]
203 pub fn error_type(&self) -> &'static str {
204 self.code.error_type()
205 }
206
207 #[must_use]
209 pub fn internal_error(message: impl Into<String>) -> Self {
210 Self::with_message(IamErrorCode::ServiceFailure, message)
211 }
212
213 #[must_use]
215 pub fn missing_action() -> Self {
216 Self::with_message(IamErrorCode::MissingAction, "Missing Action parameter")
217 }
218
219 #[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 #[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 #[must_use]
239 pub fn no_such_entity(message: impl Into<String>) -> Self {
240 Self::with_message(IamErrorCode::NoSuchEntity, message)
241 }
242
243 #[must_use]
245 pub fn entity_already_exists(message: impl Into<String>) -> Self {
246 Self::with_message(IamErrorCode::EntityAlreadyExists, message)
247 }
248
249 #[must_use]
251 pub fn delete_conflict(message: impl Into<String>) -> Self {
252 Self::with_message(IamErrorCode::DeleteConflict, message)
253 }
254
255 #[must_use]
257 pub fn limit_exceeded(message: impl Into<String>) -> Self {
258 Self::with_message(IamErrorCode::LimitExceeded, message)
259 }
260
261 #[must_use]
263 pub fn malformed_policy_document(message: impl Into<String>) -> Self {
264 Self::with_message(IamErrorCode::MalformedPolicyDocument, message)
265 }
266
267 #[must_use]
269 pub fn invalid_input(message: impl Into<String>) -> Self {
270 Self::with_message(IamErrorCode::InvalidInput, message)
271 }
272
273 #[must_use]
275 pub fn invalid_security(message: impl Into<String>) -> Self {
276 Self::with_message(IamErrorCode::InvalidInput, message)
277 }
278}
279
280#[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}