systemprompt_models/api/
responses.rs

1use super::pagination::PaginationInfo;
2use chrono::{DateTime, Utc};
3use indexmap::IndexMap;
4use serde::{Deserialize, Serialize};
5
6#[cfg(feature = "web")]
7use axum::http::StatusCode;
8#[cfg(feature = "web")]
9use axum::response::IntoResponse;
10#[cfg(feature = "web")]
11use axum::Json;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct ResponseLinks {
15    pub self_link: String,
16
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub next: Option<String>,
19
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub prev: Option<String>,
22
23    pub docs: String,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct ResponseMeta {
28    pub timestamp: DateTime<Utc>,
29
30    pub version: String,
31
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub pagination: Option<PaginationInfo>,
34}
35
36impl ResponseMeta {
37    pub fn new() -> Self {
38        Self {
39            timestamp: Utc::now(),
40            version: "1.0.0".to_string(),
41            pagination: None,
42        }
43    }
44
45    pub fn with_pagination(mut self, pagination: PaginationInfo) -> Self {
46        self.pagination = Some(pagination);
47        self
48    }
49}
50
51impl Default for ResponseMeta {
52    fn default() -> Self {
53        Self::new()
54    }
55}
56
57#[derive(Debug, Serialize, Deserialize)]
58pub struct ApiResponse<T>
59where
60    T: 'static,
61{
62    pub data: T,
63
64    pub meta: ResponseMeta,
65
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub links: Option<ResponseLinks>,
68}
69
70impl<T: Serialize + 'static> ApiResponse<T> {
71    pub fn new(data: T) -> Self {
72        Self {
73            data,
74            meta: ResponseMeta::new(),
75            links: None,
76        }
77    }
78
79    pub fn with_links(mut self, links: ResponseLinks) -> Self {
80        self.links = Some(links);
81        self
82    }
83
84    pub fn with_meta(mut self, meta: ResponseMeta) -> Self {
85        self.meta = meta;
86        self
87    }
88}
89
90#[derive(Debug, Serialize, Deserialize)]
91pub struct SingleResponse<T>
92where
93    T: 'static,
94{
95    pub data: T,
96
97    pub meta: ResponseMeta,
98
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub links: Option<ResponseLinks>,
101}
102
103impl<T: Serialize + 'static> SingleResponse<T> {
104    pub fn new(data: T) -> Self {
105        Self {
106            data,
107            meta: ResponseMeta::new(),
108            links: None,
109        }
110    }
111
112    pub const fn with_meta(data: T, meta: ResponseMeta) -> Self {
113        Self {
114            data,
115            meta,
116            links: None,
117        }
118    }
119
120    pub fn with_links(mut self, links: ResponseLinks) -> Self {
121        self.links = Some(links);
122        self
123    }
124}
125
126#[derive(Debug, Serialize, Deserialize)]
127pub struct CollectionResponse<T>
128where
129    T: 'static,
130{
131    pub data: Vec<T>,
132
133    pub meta: ResponseMeta,
134
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub links: Option<ResponseLinks>,
137}
138
139impl<T: Serialize + 'static> CollectionResponse<T> {
140    pub fn new(data: Vec<T>) -> Self {
141        Self {
142            data,
143            meta: ResponseMeta::new(),
144            links: None,
145        }
146    }
147
148    pub fn paginated(data: Vec<T>, pagination: PaginationInfo) -> Self {
149        Self {
150            data,
151            meta: ResponseMeta::new().with_pagination(pagination),
152            links: None,
153        }
154    }
155
156    pub fn with_links(mut self, links: ResponseLinks) -> Self {
157        self.links = Some(links);
158        self
159    }
160}
161
162#[derive(Debug, Serialize, Deserialize)]
163pub struct SuccessResponse {
164    pub message: String,
165
166    pub meta: ResponseMeta,
167}
168
169impl SuccessResponse {
170    pub fn new(message: impl Into<String>) -> Self {
171        Self {
172            message: message.into(),
173            meta: ResponseMeta::new(),
174        }
175    }
176}
177
178#[derive(Debug, Serialize, Deserialize)]
179pub struct CreatedResponse<T>
180where
181    T: 'static,
182{
183    pub data: T,
184
185    pub meta: ResponseMeta,
186
187    pub location: String,
188}
189
190impl<T: Serialize + 'static> CreatedResponse<T> {
191    pub fn new(data: T, location: impl Into<String>) -> Self {
192        Self {
193            data,
194            meta: ResponseMeta::new(),
195            location: location.into(),
196        }
197    }
198}
199
200#[derive(Debug, Serialize, Deserialize)]
201pub struct AcceptedResponse {
202    pub message: String,
203
204    pub job_id: Option<String>,
205
206    pub status_url: Option<String>,
207
208    pub meta: ResponseMeta,
209}
210
211impl AcceptedResponse {
212    pub fn new(message: impl Into<String>) -> Self {
213        Self {
214            message: message.into(),
215            job_id: None,
216            status_url: None,
217            meta: ResponseMeta::new(),
218        }
219    }
220
221    pub fn with_job(mut self, job_id: impl Into<String>, status_url: impl Into<String>) -> Self {
222        self.job_id = Some(job_id.into());
223        self.status_url = Some(status_url.into());
224        self
225    }
226}
227
228#[derive(Debug, Serialize, Deserialize)]
229pub struct Link {
230    pub href: String,
231    #[serde(skip_serializing_if = "Option::is_none")]
232    pub title: Option<String>,
233}
234
235impl Link {
236    pub fn new(href: impl Into<String>, title: Option<String>) -> Self {
237        Self {
238            href: href.into(),
239            title,
240        }
241    }
242}
243
244#[derive(Debug, Serialize, Deserialize)]
245pub struct DiscoveryResponse<T>
246where
247    T: 'static,
248{
249    pub data: T,
250    pub meta: ResponseMeta,
251    #[serde(rename = "_links")]
252    pub links: IndexMap<String, Link>,
253}
254
255impl<T: Serialize + 'static> DiscoveryResponse<T> {
256    pub fn new(data: T, links: IndexMap<String, Link>) -> Self {
257        Self {
258            data,
259            meta: ResponseMeta::new(),
260            links,
261        }
262    }
263
264    pub fn with_meta(mut self, meta: ResponseMeta) -> Self {
265        self.meta = meta;
266        self
267    }
268}
269
270#[cfg(feature = "web")]
271impl<T: Serialize + 'static> IntoResponse for SingleResponse<T> {
272    fn into_response(self) -> axum::response::Response {
273        (StatusCode::OK, Json(self)).into_response()
274    }
275}
276
277#[cfg(feature = "web")]
278impl<T: Serialize + 'static> IntoResponse for CollectionResponse<T> {
279    fn into_response(self) -> axum::response::Response {
280        (StatusCode::OK, Json(self)).into_response()
281    }
282}
283
284#[cfg(feature = "web")]
285impl IntoResponse for SuccessResponse {
286    fn into_response(self) -> axum::response::Response {
287        (StatusCode::OK, Json(self)).into_response()
288    }
289}
290
291#[cfg(feature = "web")]
292impl<T: Serialize + 'static> IntoResponse for CreatedResponse<T> {
293    fn into_response(self) -> axum::response::Response {
294        (
295            StatusCode::CREATED,
296            [("Location", self.location.clone())],
297            Json(self),
298        )
299            .into_response()
300    }
301}
302
303#[cfg(feature = "web")]
304impl IntoResponse for AcceptedResponse {
305    fn into_response(self) -> axum::response::Response {
306        (StatusCode::ACCEPTED, Json(self)).into_response()
307    }
308}