Skip to main content

awsim_core/
error.rs

1use axum::http::StatusCode;
2use serde::Serialize;
3
4/// Represents an AWS API error response.
5#[derive(Debug, Clone, Serialize)]
6pub struct AwsError {
7    /// HTTP status code (e.g., 404, 400, 500)
8    #[serde(skip)]
9    pub status: StatusCode,
10
11    /// AWS error code (e.g., "NoSuchBucket", "ResourceNotFoundException")
12    pub code: String,
13
14    /// Human-readable error message
15    pub message: String,
16
17    /// Error type: "Sender" (client error) or "Receiver" (server error)
18    pub error_type: ErrorType,
19}
20
21#[derive(Debug, Clone, Serialize)]
22pub enum ErrorType {
23    Sender,
24    Receiver,
25}
26
27impl AwsError {
28    pub fn not_found(code: impl Into<String>, message: impl Into<String>) -> Self {
29        Self {
30            status: StatusCode::NOT_FOUND,
31            code: code.into(),
32            message: message.into(),
33            error_type: ErrorType::Sender,
34        }
35    }
36
37    pub fn bad_request(code: impl Into<String>, message: impl Into<String>) -> Self {
38        Self {
39            status: StatusCode::BAD_REQUEST,
40            code: code.into(),
41            message: message.into(),
42            error_type: ErrorType::Sender,
43        }
44    }
45
46    pub fn conflict(code: impl Into<String>, message: impl Into<String>) -> Self {
47        Self {
48            status: StatusCode::CONFLICT,
49            code: code.into(),
50            message: message.into(),
51            error_type: ErrorType::Sender,
52        }
53    }
54
55    pub fn internal(message: impl Into<String>) -> Self {
56        Self {
57            status: StatusCode::INTERNAL_SERVER_ERROR,
58            code: "InternalServiceError".to_string(),
59            message: message.into(),
60            error_type: ErrorType::Receiver,
61        }
62    }
63
64    pub fn not_implemented(operation: &str) -> Self {
65        Self {
66            status: StatusCode::NOT_IMPLEMENTED,
67            code: "NotImplemented".to_string(),
68            message: format!("Operation '{operation}' is not yet implemented in AWSim"),
69            error_type: ErrorType::Receiver,
70        }
71    }
72
73    pub fn unknown_operation(operation: &str) -> Self {
74        Self {
75            status: StatusCode::BAD_REQUEST,
76            code: "UnknownOperationException".to_string(),
77            message: format!("Unknown operation: {operation}"),
78            error_type: ErrorType::Sender,
79        }
80    }
81
82    pub fn access_denied(message: impl Into<String>) -> Self {
83        Self {
84            status: StatusCode::FORBIDDEN,
85            code: "AccessDeniedException".to_string(),
86            message: message.into(),
87            error_type: ErrorType::Sender,
88        }
89    }
90
91    pub fn access_denied_for(action: &str, principal_arn: &str, resource: &str) -> Self {
92        Self {
93            status: StatusCode::FORBIDDEN,
94            code: "AccessDenied".to_string(),
95            message: format!(
96                "User: {principal_arn} is not authorized to perform: {action} on resource: {resource}"
97            ),
98            error_type: ErrorType::Sender,
99        }
100    }
101
102    pub fn validation(message: impl Into<String>) -> Self {
103        Self {
104            status: StatusCode::BAD_REQUEST,
105            code: "ValidationException".to_string(),
106            message: message.into(),
107            error_type: ErrorType::Sender,
108        }
109    }
110}
111
112impl std::fmt::Display for AwsError {
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        write!(f, "{}: {}", self.code, self.message)
115    }
116}
117
118impl std::error::Error for AwsError {}