1use async_trait::async_trait;
2use bytes::Bytes;
3use http::{HeaderMap, Method, StatusCode};
4use std::collections::HashMap;
5
6#[derive(Debug)]
8pub struct AwsRequest {
9 pub service: String,
10 pub action: String,
11 pub region: String,
12 pub account_id: String,
13 pub request_id: String,
14 pub headers: HeaderMap,
15 pub query_params: HashMap<String, String>,
16 pub body: Bytes,
17 pub path_segments: Vec<String>,
18 pub raw_path: String,
20 pub raw_query: String,
22 pub method: Method,
23 pub is_query_protocol: bool,
25 pub access_key_id: Option<String>,
27}
28
29impl AwsRequest {
30 pub fn json_body(&self) -> serde_json::Value {
32 serde_json::from_slice(&self.body).unwrap_or(serde_json::Value::Null)
33 }
34}
35
36pub struct AwsResponse {
38 pub status: StatusCode,
39 pub content_type: String,
40 pub body: Bytes,
41 pub headers: HeaderMap,
42}
43
44impl AwsResponse {
45 pub fn xml(status: StatusCode, body: impl Into<Bytes>) -> Self {
46 Self {
47 status,
48 content_type: "text/xml".to_string(),
49 body: body.into(),
50 headers: HeaderMap::new(),
51 }
52 }
53
54 pub fn json(status: StatusCode, body: impl Into<Bytes>) -> Self {
55 Self {
56 status,
57 content_type: "application/x-amz-json-1.1".to_string(),
58 body: body.into(),
59 headers: HeaderMap::new(),
60 }
61 }
62
63 pub fn ok_json(value: serde_json::Value) -> Self {
65 Self::json(StatusCode::OK, serde_json::to_vec(&value).unwrap())
66 }
67}
68
69#[derive(Debug, thiserror::Error)]
71pub enum AwsServiceError {
72 #[error("service not found: {service}")]
73 ServiceNotFound { service: String },
74
75 #[error("action {action} not implemented for service {service}")]
76 ActionNotImplemented { service: String, action: String },
77
78 #[error("{code}: {message}")]
79 AwsError {
80 status: StatusCode,
81 code: String,
82 message: String,
83 extra_fields: Vec<(String, String)>,
85 headers: Vec<(String, String)>,
87 },
88}
89
90impl AwsServiceError {
91 pub fn action_not_implemented(service: &str, action: &str) -> Self {
92 Self::ActionNotImplemented {
93 service: service.to_string(),
94 action: action.to_string(),
95 }
96 }
97
98 pub fn aws_error(
99 status: StatusCode,
100 code: impl Into<String>,
101 message: impl Into<String>,
102 ) -> Self {
103 Self::AwsError {
104 status,
105 code: code.into(),
106 message: message.into(),
107 extra_fields: Vec::new(),
108 headers: Vec::new(),
109 }
110 }
111
112 pub fn aws_error_with_fields(
113 status: StatusCode,
114 code: impl Into<String>,
115 message: impl Into<String>,
116 extra_fields: Vec<(String, String)>,
117 ) -> Self {
118 Self::AwsError {
119 status,
120 code: code.into(),
121 message: message.into(),
122 extra_fields,
123 headers: Vec::new(),
124 }
125 }
126
127 pub fn aws_error_with_headers(
128 status: StatusCode,
129 code: impl Into<String>,
130 message: impl Into<String>,
131 headers: Vec<(String, String)>,
132 ) -> Self {
133 Self::AwsError {
134 status,
135 code: code.into(),
136 message: message.into(),
137 extra_fields: Vec::new(),
138 headers,
139 }
140 }
141
142 pub fn extra_fields(&self) -> &[(String, String)] {
143 match self {
144 Self::AwsError { extra_fields, .. } => extra_fields,
145 _ => &[],
146 }
147 }
148
149 pub fn status(&self) -> StatusCode {
150 match self {
151 Self::ServiceNotFound { .. } => StatusCode::BAD_REQUEST,
152 Self::ActionNotImplemented { .. } => StatusCode::NOT_IMPLEMENTED,
153 Self::AwsError { status, .. } => *status,
154 }
155 }
156
157 pub fn code(&self) -> &str {
158 match self {
159 Self::ServiceNotFound { .. } => "UnknownService",
160 Self::ActionNotImplemented { .. } => "InvalidAction",
161 Self::AwsError { code, .. } => code,
162 }
163 }
164
165 pub fn message(&self) -> String {
166 match self {
167 Self::ServiceNotFound { service } => format!("service not found: {service}"),
168 Self::ActionNotImplemented { service, action } => {
169 format!("action {action} not implemented for service {service}")
170 }
171 Self::AwsError { message, .. } => message.clone(),
172 }
173 }
174
175 pub fn response_headers(&self) -> &[(String, String)] {
176 match self {
177 Self::AwsError { headers, .. } => headers,
178 _ => &[],
179 }
180 }
181}
182
183#[async_trait]
185pub trait AwsService: Send + Sync {
186 fn service_name(&self) -> &str;
188
189 async fn handle(&self, request: AwsRequest) -> Result<AwsResponse, AwsServiceError>;
191
192 fn supported_actions(&self) -> &[&str];
194}