1use axum::http::StatusCode;
2use serde::Serialize;
3use serde_json::{Map, Value};
4
5#[derive(Debug, Clone, Serialize)]
7pub struct AwsError {
8 #[serde(skip)]
10 pub status: StatusCode,
11
12 pub code: String,
14
15 pub message: String,
17
18 pub error_type: ErrorType,
20
21 #[serde(skip_serializing_if = "Option::is_none")]
32 pub extras: Option<Box<Map<String, Value>>>,
33}
34
35#[derive(Debug, Clone, Serialize)]
36pub enum ErrorType {
37 Sender,
38 Receiver,
39}
40
41impl AwsError {
42 pub fn not_found(code: impl Into<String>, message: impl Into<String>) -> Self {
43 Self {
44 status: StatusCode::NOT_FOUND,
45 code: code.into(),
46 message: message.into(),
47 error_type: ErrorType::Sender,
48 extras: None,
49 }
50 }
51
52 pub fn service_not_found(code: impl Into<String>, message: impl Into<String>) -> Self {
60 Self {
61 status: StatusCode::BAD_REQUEST,
62 code: code.into(),
63 message: message.into(),
64 error_type: ErrorType::Sender,
65 extras: None,
66 }
67 }
68
69 pub fn bad_request(code: impl Into<String>, message: impl Into<String>) -> Self {
70 Self {
71 status: StatusCode::BAD_REQUEST,
72 code: code.into(),
73 message: message.into(),
74 error_type: ErrorType::Sender,
75 extras: None,
76 }
77 }
78
79 pub fn conflict(code: impl Into<String>, message: impl Into<String>) -> Self {
80 Self {
81 status: StatusCode::CONFLICT,
82 code: code.into(),
83 message: message.into(),
84 error_type: ErrorType::Sender,
85 extras: None,
86 }
87 }
88
89 pub fn internal(message: impl Into<String>) -> Self {
90 Self {
91 status: StatusCode::INTERNAL_SERVER_ERROR,
92 code: "InternalServiceError".to_string(),
93 message: message.into(),
94 error_type: ErrorType::Receiver,
95 extras: None,
96 }
97 }
98
99 pub fn not_implemented(operation: &str) -> Self {
100 Self {
101 status: StatusCode::NOT_IMPLEMENTED,
102 code: "NotImplemented".to_string(),
103 message: format!("Operation '{operation}' is not yet implemented in AWSim"),
104 error_type: ErrorType::Receiver,
105 extras: None,
106 }
107 }
108
109 pub fn unknown_operation(operation: &str) -> Self {
110 Self {
111 status: StatusCode::BAD_REQUEST,
112 code: "UnknownOperationException".to_string(),
113 message: format!("Unknown operation: {operation}"),
114 error_type: ErrorType::Sender,
115 extras: None,
116 }
117 }
118
119 pub fn access_denied(message: impl Into<String>) -> Self {
120 Self {
121 status: StatusCode::FORBIDDEN,
122 code: "AccessDeniedException".to_string(),
123 message: message.into(),
124 error_type: ErrorType::Sender,
125 extras: None,
126 }
127 }
128
129 pub fn access_denied_for(action: &str, principal_arn: &str, resource: &str) -> Self {
130 Self {
131 status: StatusCode::FORBIDDEN,
132 code: "AccessDenied".to_string(),
133 message: format!(
134 "User: {principal_arn} is not authorized to perform: {action} on resource: {resource}"
135 ),
136 error_type: ErrorType::Sender,
137 extras: None,
138 }
139 }
140
141 pub fn validation(message: impl Into<String>) -> Self {
142 Self {
143 status: StatusCode::BAD_REQUEST,
144 code: "ValidationException".to_string(),
145 message: message.into(),
146 error_type: ErrorType::Sender,
147 extras: None,
148 }
149 }
150
151 pub fn with_extras(mut self, extras: Map<String, Value>) -> Self {
153 self.extras = Some(Box::new(extras));
154 self
155 }
156
157 pub fn with_extra(mut self, key: impl Into<String>, value: Value) -> Self {
159 self.extras
160 .get_or_insert_with(|| Box::new(Map::new()))
161 .insert(key.into(), value);
162 self
163 }
164}
165
166impl std::fmt::Display for AwsError {
167 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168 write!(f, "{}: {}", self.code, self.message)
169 }
170}
171
172impl std::error::Error for AwsError {}