aws_lambda_router/
response.rs

1use serde::{Deserialize, Serialize};
2use serde_json::{json, Value};
3use std::collections::HashMap;
4
5/// HTTP Response builder
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Response {
8    #[serde(rename = "statusCode")]
9    pub status_code: u16,
10    pub headers: HashMap<String, String>,
11    pub body: String,
12    #[serde(rename = "isBase64Encoded")]
13    pub is_base64_encoded: bool,
14}
15
16impl Response {
17    /// Create a new Response
18    pub fn new(status_code: u16) -> Self {
19        let mut headers = HashMap::new();
20        headers.insert("Content-Type".to_string(), "application/json".to_string());
21
22        Self {
23            status_code,
24            headers,
25            body: String::new(),
26            is_base64_encoded: false,
27        }
28    }
29
30    /// Set response body from JSON value
31    pub fn json(mut self, body: Value) -> Self {
32        self.body = body.to_string();
33        self.headers
34            .insert("Content-Type".to_string(), "application/json".to_string());
35        self
36    }
37
38    /// Set response body from serializable object
39    pub fn json_body<T: Serialize>(mut self, body: &T) -> Self {
40        self.body = serde_json::to_string(body).unwrap_or_else(|_| "{}".to_string());
41        self.headers
42            .insert("Content-Type".to_string(), "application/json".to_string());
43        self
44    }
45
46    /// Set response body as string
47    pub fn text(mut self, body: impl Into<String>) -> Self {
48        self.body = body.into();
49        self.headers
50            .insert("Content-Type".to_string(), "text/plain".to_string());
51        self
52    }
53
54    /// Add header
55    pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
56        self.headers.insert(key.into(), value.into());
57        self
58    }
59
60    /// Add multiple headers
61    pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
62        self.headers.extend(headers);
63        self
64    }
65
66    /// Add CORS headers
67    pub fn with_cors(mut self) -> Self {
68        self.headers
69            .insert("Access-Control-Allow-Origin".to_string(), "*".to_string());
70        self.headers.insert(
71            "Access-Control-Allow-Methods".to_string(),
72            "GET, POST, PUT, DELETE, OPTIONS".to_string(),
73        );
74        self.headers.insert(
75            "Access-Control-Allow-Headers".to_string(),
76            "Content-Type, Authorization".to_string(),
77        );
78        self.headers
79            .insert("Access-Control-Max-Age".to_string(), "3600".to_string());
80        self
81    }
82
83    /// Convert to JSON value
84    pub fn to_json(&self) -> Value {
85        json!({
86            "statusCode": self.status_code,
87            "headers": self.headers,
88            "body": self.body,
89            "isBase64Encoded": self.is_base64_encoded
90        })
91    }
92
93    /// Create Response from JSON value (for controller compatibility)
94    /// Expects format: { statusCode: number, headers: object, body: any }
95    pub fn from_json_value(value: Value) -> Self {
96        let status_code = value["statusCode"].as_u64().unwrap_or(200) as u16;
97
98        let mut headers = HashMap::new();
99        if let Some(headers_obj) = value["headers"].as_object() {
100            for (key, val) in headers_obj {
101                if let Some(s) = val.as_str() {
102                    headers.insert(key.clone(), s.to_string());
103                }
104            }
105        }
106
107        // Handle body - could be already stringified or an object
108        let body = if let Some(body_str) = value["body"].as_str() {
109            body_str.to_string()
110        } else {
111            // If body is an object, serialize it to JSON string
112            serde_json::to_string(&value["body"]).unwrap_or_else(|_| value["body"].to_string())
113        };
114
115        Self {
116            status_code,
117            headers,
118            body,
119            is_base64_encoded: value["isBase64Encoded"].as_bool().unwrap_or(false),
120        }
121    }
122
123    // Convenience constructors
124
125    /// 200 OK response
126    pub fn ok(body: Value) -> Self {
127        Self::new(200).json(body).with_cors()
128    }
129
130    /// 201 Created response
131    pub fn created(body: Value) -> Self {
132        Self::new(201).json(body).with_cors()
133    }
134
135    /// 204 No Content response
136    pub fn no_content() -> Self {
137        Self::new(204).with_cors()
138    }
139
140    /// 400 Bad Request response
141    pub fn bad_request(message: &str) -> Self {
142        Self::new(400)
143            .json(json!({
144                "error": "Bad Request",
145                "message": message
146            }))
147            .with_cors()
148    }
149
150    /// 401 Unauthorized response
151    pub fn unauthorized(message: &str) -> Self {
152        Self::new(401)
153            .json(json!({
154                "error": "Unauthorized",
155                "message": message
156            }))
157            .with_cors()
158    }
159
160    /// 403 Forbidden response
161    pub fn forbidden(message: &str) -> Self {
162        Self::new(403)
163            .json(json!({
164                "error": "Forbidden",
165                "message": message
166            }))
167            .with_cors()
168    }
169
170    /// 404 Not Found response
171    pub fn not_found(message: &str) -> Self {
172        Self::new(404)
173            .json(json!({
174                "error": "Not Found",
175                "message": message
176            }))
177            .with_cors()
178    }
179
180    /// 405 Method Not Allowed response
181    pub fn method_not_allowed(message: &str) -> Self {
182        Self::new(405)
183            .json(json!({
184                "error": "Method Not Allowed",
185                "message": message
186            }))
187            .with_cors()
188    }
189
190    /// 500 Internal Server Error response
191    pub fn internal_error(message: &str) -> Self {
192        Self::new(500)
193            .json(json!({
194                "error": "Internal Server Error",
195                "message": message
196            }))
197            .with_cors()
198    }
199
200    /// CORS preflight response
201    pub fn cors_preflight() -> Self {
202        Self::new(200).text("").with_cors()
203    }
204}