quokka_handler/
response.rs1use std::collections::HashMap;
2
3use axum::{http::StatusCode, response::IntoResponse, Json};
4
5#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
16pub struct JsonResponse<D = serde_json::Value, M = DefaultMeta> {
17 #[serde(skip_serializing_if = "Option::is_none", default = "Option::default")]
18 pub data: Option<D>,
19 #[serde(skip_serializing_if = "Vec::is_empty", default)]
20 pub errors: Vec<ErrorObject>,
21 #[serde(skip_serializing_if = "Option::is_none", default)]
22 pub meta: Option<M>,
23 #[serde(skip, default)]
24 pub status_code: u16,
25}
26
27pub type DefaultMeta = HashMap<String, serde_json::Value>;
28
29#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
30pub struct ErrorObject<M = serde_json::Value> {
31 #[serde(skip_serializing_if = "String::is_empty", default)]
32 pub id: String,
33 #[serde(skip_serializing_if = "HashMap::is_empty", default)]
34 pub links: HashMap<String, Link>,
35 pub status: String,
36 #[serde(skip_serializing_if = "String::is_empty", default)]
37 pub title: String,
38 #[serde(skip_serializing_if = "String::is_empty", default)]
39 pub detail: String,
40 pub source: Source,
41 #[serde(skip_serializing_if = "Option::is_none", default)]
42 pub meta: Option<M>,
43}
44
45#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
46pub struct Source {
47 #[serde(skip_serializing_if = "String::is_empty", default)]
48 pub pointer: String,
49 #[serde(skip_serializing_if = "String::is_empty", default)]
50 pub parameter: String,
51 #[serde(skip_serializing_if = "String::is_empty", default)]
52 pub header: String,
53}
54
55#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
56pub enum Link<M = DefaultMeta> {
57 String(String),
58 Link {
59 href: String,
60 #[serde(skip_serializing_if = "String::is_empty", default)]
61 rel: String,
62 #[serde(skip_serializing_if = "String::is_empty", default)]
63 title: String,
64 #[serde(rename = "type")]
65 typ: String,
66 #[serde(skip_serializing_if = "String::is_empty", default)]
67 hreflang: String,
68 #[serde(skip_serializing_if = "Option::is_none", default)]
69 meta: Option<M>,
70 },
71}
72
73impl JsonResponse<(), serde_json::Value> {
74 pub fn no_data(message: impl ToString) -> Self {
78 Self {
79 status_code: 204,
80 meta: Some(serde_json::json! {{
81 "message": message.to_string(),
82 }}),
83 ..Default::default()
84 }
85 }
86}
87
88impl<D> JsonResponse<D> {
89 pub fn data(data: D) -> Self {
90 Self {
91 status_code: 200,
92 data: Some(data),
93 ..Default::default()
94 }
95 }
96}
97
98impl<D: serde::Serialize, M: serde::Serialize> IntoResponse for JsonResponse<D, M> {
99 fn into_response(self) -> axum::response::Response {
100 let status = self.status_code;
101
102 let mut response = Json(self).into_response();
103
104 if status >= 100 {
105 *response.status_mut() = StatusCode::from_u16(status).unwrap_or_default();
106 }
107
108 response
109 }
110}
111
112impl<D, M> JsonResponse<D, M> {
113 pub fn error(status: u16, title: impl ToString, detail: impl ToString) -> Self {
114 let mut me = Self {
115 status_code: status,
116 meta: None,
117 data: None,
118 ..Default::default()
119 };
120 me.add_error(ErrorObject {
121 status: status.to_string(),
122 title: title.to_string(),
123 detail: detail.to_string(),
124 ..Default::default()
125 });
126
127 me
128 }
129
130 pub fn add_error(&mut self, error: ErrorObject) -> &mut Self {
131 self.errors.push(error);
132
133 self
134 }
135}
136
137impl<D, M> Default for JsonResponse<D, M> {
138 fn default() -> Self {
139 Self {
140 data: Default::default(),
141 errors: Default::default(),
142 meta: Default::default(),
143 status_code: Default::default(),
144 }
145 }
146}