1use std::fmt;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
10#[non_exhaustive]
11pub enum KmsErrorCode {
12 #[default]
14 AlreadyExistsException,
15 CloudHsmClusterInvalidConfigurationException,
17 CustomKeyStoreInvalidStateException,
19 CustomKeyStoreNotFoundException,
21 DependencyTimeoutException,
23 DisabledException,
25 DryRunOperationException,
27 IncorrectKeyException,
29 InvalidAction,
31 InvalidAliasNameException,
33 InvalidArnException,
35 InvalidCiphertextException,
37 InvalidGrantIdException,
39 InvalidGrantTokenException,
41 InvalidKeyUsageException,
43 InvalidMarkerException,
45 KMSInternalException,
47 KMSInvalidMacException,
49 KMSInvalidSignatureException,
51 KMSInvalidStateException,
53 KeyUnavailableException,
55 LimitExceededException,
57 MalformedPolicyDocumentException,
59 MissingAction,
61 NotFoundException,
63 TagException,
65 UnsupportedOperationException,
67 XksKeyAlreadyInUseException,
69 XksKeyInvalidConfigurationException,
71 XksKeyNotFoundException,
73}
74
75impl KmsErrorCode {
76 #[must_use]
78 pub fn error_type(&self) -> &'static str {
79 self.as_str()
80 }
81
82 #[must_use]
84 pub fn as_str(&self) -> &'static str {
85 match self {
86 Self::AlreadyExistsException => "AlreadyExistsException",
87 Self::CloudHsmClusterInvalidConfigurationException => {
88 "CloudHsmClusterInvalidConfigurationException"
89 }
90 Self::CustomKeyStoreInvalidStateException => "CustomKeyStoreInvalidStateException",
91 Self::CustomKeyStoreNotFoundException => "CustomKeyStoreNotFoundException",
92 Self::DependencyTimeoutException => "DependencyTimeoutException",
93 Self::DisabledException => "DisabledException",
94 Self::DryRunOperationException => "DryRunOperationException",
95 Self::IncorrectKeyException => "IncorrectKeyException",
96 Self::InvalidAction => "InvalidAction",
97 Self::InvalidAliasNameException => "InvalidAliasNameException",
98 Self::InvalidArnException => "InvalidArnException",
99 Self::InvalidCiphertextException => "InvalidCiphertextException",
100 Self::InvalidGrantIdException => "InvalidGrantIdException",
101 Self::InvalidGrantTokenException => "InvalidGrantTokenException",
102 Self::InvalidKeyUsageException => "InvalidKeyUsageException",
103 Self::InvalidMarkerException => "InvalidMarkerException",
104 Self::KMSInternalException => "KMSInternalException",
105 Self::KMSInvalidMacException => "KMSInvalidMacException",
106 Self::KMSInvalidSignatureException => "KMSInvalidSignatureException",
107 Self::KMSInvalidStateException => "KMSInvalidStateException",
108 Self::KeyUnavailableException => "KeyUnavailableException",
109 Self::LimitExceededException => "LimitExceededException",
110 Self::MalformedPolicyDocumentException => "MalformedPolicyDocumentException",
111 Self::MissingAction => "MissingAction",
112 Self::NotFoundException => "NotFoundException",
113 Self::TagException => "TagException",
114 Self::UnsupportedOperationException => "UnsupportedOperationException",
115 Self::XksKeyAlreadyInUseException => "XksKeyAlreadyInUseException",
116 Self::XksKeyInvalidConfigurationException => "XksKeyInvalidConfigurationException",
117 Self::XksKeyNotFoundException => "XksKeyNotFoundException",
118 }
119 }
120
121 #[must_use]
123 pub fn default_status_code(&self) -> http::StatusCode {
124 match self {
125 Self::CloudHsmClusterInvalidConfigurationException
126 | Self::CustomKeyStoreInvalidStateException
127 | Self::CustomKeyStoreNotFoundException
128 | Self::IncorrectKeyException
129 | Self::InvalidAction
130 | Self::InvalidAliasNameException
131 | Self::InvalidArnException
132 | Self::InvalidCiphertextException
133 | Self::InvalidGrantIdException
134 | Self::InvalidGrantTokenException
135 | Self::InvalidKeyUsageException
136 | Self::InvalidMarkerException
137 | Self::KMSInvalidMacException
138 | Self::KMSInvalidSignatureException
139 | Self::LimitExceededException
140 | Self::MalformedPolicyDocumentException
141 | Self::MissingAction
142 | Self::TagException
143 | Self::UnsupportedOperationException
144 | Self::XksKeyAlreadyInUseException
145 | Self::XksKeyInvalidConfigurationException
146 | Self::XksKeyNotFoundException => http::StatusCode::BAD_REQUEST,
147 Self::NotFoundException => http::StatusCode::NOT_FOUND,
148 Self::AlreadyExistsException
149 | Self::DisabledException
150 | Self::KMSInvalidStateException => http::StatusCode::CONFLICT,
151 Self::DryRunOperationException => http::StatusCode::PRECONDITION_FAILED,
152 Self::KMSInternalException | Self::KeyUnavailableException => {
153 http::StatusCode::INTERNAL_SERVER_ERROR
154 }
155 Self::DependencyTimeoutException => http::StatusCode::SERVICE_UNAVAILABLE,
156 }
157 }
158}
159
160impl fmt::Display for KmsErrorCode {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 f.write_str(self.as_str())
163 }
164}
165
166#[derive(Debug)]
168pub struct KmsError {
169 pub code: KmsErrorCode,
171 pub message: String,
173 pub status_code: http::StatusCode,
175 pub source: Option<Box<dyn std::error::Error + Send + Sync>>,
177}
178
179impl fmt::Display for KmsError {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 write!(f, "KmsError({}): {}", self.code, self.message)
182 }
183}
184
185impl std::error::Error for KmsError {
186 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
187 self.source
188 .as_ref()
189 .map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
190 }
191}
192
193impl KmsError {
194 #[must_use]
196 pub fn new(code: KmsErrorCode) -> Self {
197 Self {
198 status_code: code.default_status_code(),
199 message: code.as_str().to_owned(),
200 code,
201 source: None,
202 }
203 }
204
205 #[must_use]
207 pub fn with_message(code: KmsErrorCode, message: impl Into<String>) -> Self {
208 Self {
209 status_code: code.default_status_code(),
210 message: message.into(),
211 code,
212 source: None,
213 }
214 }
215
216 #[must_use]
218 pub fn error_type(&self) -> &'static str {
219 self.code.error_type()
220 }
221
222 #[must_use]
224 pub fn internal_error(message: impl Into<String>) -> Self {
225 Self::with_message(KmsErrorCode::KMSInternalException, message)
226 }
227
228 #[must_use]
230 pub fn validation(message: impl Into<String>) -> Self {
231 Self::with_message(KmsErrorCode::InvalidArnException, message)
232 }
233
234 #[must_use]
236 pub fn missing_action() -> Self {
237 Self::with_message(
238 KmsErrorCode::MissingAction,
239 "Missing required header: X-Amz-Target",
240 )
241 }
242
243 #[must_use]
245 pub fn unknown_operation(target: &str) -> Self {
246 Self::with_message(
247 KmsErrorCode::InvalidAction,
248 format!("Operation {target} is not supported."),
249 )
250 }
251
252 #[must_use]
254 pub fn not_implemented(operation: &str) -> Self {
255 Self::with_message(
256 KmsErrorCode::KMSInternalException,
257 format!("Operation {operation} is not yet implemented"),
258 )
259 }
260}
261
262#[macro_export]
271macro_rules! kms_error {
272 ($code:ident) => {
273 $crate::error::KmsError::new($crate::error::KmsErrorCode::$code)
274 };
275 ($code:ident, $msg:expr) => {
276 $crate::error::KmsError::with_message($crate::error::KmsErrorCode::$code, $msg)
277 };
278}