Skip to main content

systemprompt_models/api/responses/
specialized.rs

1//! Specialized response envelopes for `201 Created`, `202 Accepted`,
2//! plain success messages, and HATEOAS-style discovery payloads.
3
4use indexmap::IndexMap;
5use serde::{Deserialize, Serialize};
6
7use super::envelopes::ResponseMeta;
8
9#[cfg(feature = "web")]
10use axum::Json;
11#[cfg(feature = "web")]
12use axum::http::StatusCode;
13#[cfg(feature = "web")]
14use axum::response::IntoResponse;
15
16#[derive(Debug, Serialize, Deserialize)]
17pub struct SuccessResponse {
18    pub message: String,
19
20    pub meta: ResponseMeta,
21}
22
23impl SuccessResponse {
24    pub fn new(message: impl Into<String>) -> Self {
25        Self {
26            message: message.into(),
27            meta: ResponseMeta::new(),
28        }
29    }
30}
31
32#[derive(Debug, Serialize, Deserialize)]
33pub struct CreatedResponse<T>
34where
35    T: 'static,
36{
37    pub data: T,
38
39    pub meta: ResponseMeta,
40
41    pub location: String,
42}
43
44impl<T: Serialize + 'static> CreatedResponse<T> {
45    pub fn new(data: T, location: impl Into<String>) -> Self {
46        Self {
47            data,
48            meta: ResponseMeta::new(),
49            location: location.into(),
50        }
51    }
52}
53
54#[derive(Debug, Serialize, Deserialize)]
55pub struct AcceptedResponse {
56    pub message: String,
57
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub job_id: Option<String>,
60
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub status_url: Option<String>,
63
64    pub meta: ResponseMeta,
65}
66
67impl AcceptedResponse {
68    pub fn new(message: impl Into<String>) -> Self {
69        Self {
70            message: message.into(),
71            job_id: None,
72            status_url: None,
73            meta: ResponseMeta::new(),
74        }
75    }
76
77    #[must_use]
78    pub fn with_job(mut self, job_id: impl Into<String>, status_url: impl Into<String>) -> Self {
79        self.job_id = Some(job_id.into());
80        self.status_url = Some(status_url.into());
81        self
82    }
83}
84
85#[derive(Debug, Serialize, Deserialize)]
86pub struct Link {
87    pub href: String,
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub title: Option<String>,
90}
91
92impl Link {
93    pub fn new(href: impl Into<String>, title: Option<String>) -> Self {
94        Self {
95            href: href.into(),
96            title,
97        }
98    }
99}
100
101#[derive(Debug, Serialize, Deserialize)]
102pub struct DiscoveryResponse<T>
103where
104    T: 'static,
105{
106    pub data: T,
107    pub meta: ResponseMeta,
108    #[serde(rename = "_links")]
109    pub links: IndexMap<String, Link>,
110}
111
112impl<T: Serialize + 'static> DiscoveryResponse<T> {
113    pub fn new(data: T, links: IndexMap<String, Link>) -> Self {
114        Self {
115            data,
116            meta: ResponseMeta::new(),
117            links,
118        }
119    }
120
121    #[must_use]
122    pub fn with_meta(mut self, meta: ResponseMeta) -> Self {
123        self.meta = meta;
124        self
125    }
126}
127
128#[cfg(feature = "web")]
129impl IntoResponse for SuccessResponse {
130    fn into_response(self) -> axum::response::Response {
131        (StatusCode::OK, Json(self)).into_response()
132    }
133}
134
135#[cfg(feature = "web")]
136impl<T: Serialize + 'static> IntoResponse for CreatedResponse<T> {
137    fn into_response(self) -> axum::response::Response {
138        (
139            StatusCode::CREATED,
140            [("Location", self.location.clone())],
141            Json(self),
142        )
143            .into_response()
144    }
145}
146
147#[cfg(feature = "web")]
148impl IntoResponse for AcceptedResponse {
149    fn into_response(self) -> axum::response::Response {
150        (StatusCode::ACCEPTED, Json(self)).into_response()
151    }
152}