orbis_plugin_api/sdk/
response.rs1use super::error::{Error, Result};
4use serde::Serialize;
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, Serialize)]
9pub struct Response {
10 pub status: u16,
12
13 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
15 pub headers: HashMap<String, String>,
16
17 pub body: serde_json::Value,
19}
20
21impl Response {
22 #[inline]
24 pub fn new(status: u16, body: serde_json::Value) -> Self {
25 Self {
26 status,
27 headers: HashMap::new(),
28 body,
29 }
30 }
31
32 #[inline]
34 pub fn json<T: Serialize>(data: &T) -> Result<Self> {
35 let body = serde_json::to_value(data)?;
36 Ok(Self::new(200, body))
37 }
38
39 #[inline]
41 pub fn ok(body: serde_json::Value) -> Self {
42 Self {
43 status: 200,
44 headers: HashMap::new(),
45 body,
46 }
47 }
48
49 #[inline]
51 pub fn created<T: Serialize>(data: &T) -> Result<Self> {
52 let body = serde_json::to_value(data)?;
53 Ok(Self::new(201, body))
54 }
55
56 #[inline]
58 pub fn no_content() -> Self {
59 Self {
60 status: 204,
61 headers: HashMap::new(),
62 body: serde_json::Value::Null,
63 }
64 }
65
66 #[inline]
68 pub fn error(status: u16, message: &str) -> Self {
69 Self::new(
70 status,
71 serde_json::json!({
72 "error": true,
73 "message": message
74 }),
75 )
76 }
77
78 #[inline]
80 pub fn bad_request(message: &str) -> Self {
81 Self::error(400, message)
82 }
83
84 #[inline]
86 pub fn unauthorized(message: &str) -> Self {
87 Self::error(401, message)
88 }
89
90 #[inline]
92 pub fn forbidden(message: &str) -> Self {
93 Self::error(403, message)
94 }
95
96 #[inline]
98 pub fn not_found(message: &str) -> Self {
99 Self::error(404, message)
100 }
101
102 #[inline]
104 pub fn internal_error(message: &str) -> Self {
105 Self::error(500, message)
106 }
107
108 #[inline]
110 pub fn from_error(err: &Error) -> Self {
111 Self::error(err.status_code(), &err.to_string())
112 }
113
114 #[inline]
116 pub fn with_header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
117 self.headers.insert(name.into(), value.into());
118 self
119 }
120
121 #[inline]
123 pub fn content_type(self, content_type: &str) -> Self {
124 self.with_header("Content-Type", content_type)
125 }
126
127 #[inline]
129 pub fn cache_control(self, value: &str) -> Self {
130 self.with_header("Cache-Control", value)
131 }
132
133 #[inline]
135 pub fn no_cache(self) -> Self {
136 self.cache_control("no-store, no-cache, must-revalidate")
137 }
138
139 #[cfg(target_arch = "wasm32")]
141 pub fn to_raw(&self) -> Result<i32> {
142 let json = serde_json::to_vec(self)?;
143 Ok(super::ffi::return_bytes(&json))
144 }
145
146 #[cfg(not(target_arch = "wasm32"))]
148 pub fn to_raw(&self) -> Result<i32> {
149 Err(Error::internal("to_raw only available in WASM"))
150 }
151}
152
153impl From<Error> for Response {
154 fn from(err: Error) -> Self {
155 Self::from_error(&err)
156 }
157}
158
159#[derive(Debug, Clone, Serialize)]
161pub struct PaginatedResponse<T> {
162 pub data: Vec<T>,
164
165 pub pagination: PaginationMeta,
167}
168
169#[derive(Debug, Clone, Serialize)]
171pub struct PaginationMeta {
172 pub page: u32,
174
175 pub per_page: u32,
177
178 pub total: u64,
180
181 pub total_pages: u32,
183
184 pub has_next: bool,
186
187 pub has_prev: bool,
189}
190
191impl<T: Serialize> PaginatedResponse<T> {
192 pub fn new(data: Vec<T>, page: u32, per_page: u32, total: u64) -> Self {
194 let total_pages = ((total as f64) / (per_page as f64)).ceil() as u32;
195 Self {
196 data,
197 pagination: PaginationMeta {
198 page,
199 per_page,
200 total,
201 total_pages,
202 has_next: page < total_pages,
203 has_prev: page > 1,
204 },
205 }
206 }
207
208 pub fn into_response(self) -> Result<Response> {
210 Response::json(&self)
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn test_response_json() {
220 let data = serde_json::json!({"name": "Test"});
221 let resp = Response::json(&data).unwrap();
222
223 assert_eq!(resp.status, 200);
224 assert_eq!(resp.body["name"], "Test");
225 }
226
227 #[test]
228 fn test_response_error() {
229 let resp = Response::not_found("User not found");
230
231 assert_eq!(resp.status, 404);
232 assert_eq!(resp.body["error"], true);
233 assert_eq!(resp.body["message"], "User not found");
234 }
235
236 #[test]
237 fn test_paginated_response() {
238 let items = vec![1, 2, 3];
239 let paginated = PaginatedResponse::new(items, 2, 10, 35);
240
241 assert_eq!(paginated.pagination.page, 2);
242 assert_eq!(paginated.pagination.total_pages, 4);
243 assert!(paginated.pagination.has_next);
244 assert!(paginated.pagination.has_prev);
245 }
246}