use std::collections::HashMap;
use axum::{http::StatusCode, response::IntoResponse, Json};
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct JsonResponse<D = serde_json::Value, M = DefaultMeta> {
#[serde(skip_serializing_if = "Option::is_none", default = "Option::default")]
pub data: Option<D>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub errors: Vec<ErrorObject>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub meta: Option<M>,
#[serde(skip, default)]
pub status_code: u16,
}
pub type DefaultMeta = HashMap<String, serde_json::Value>;
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
pub struct ErrorObject<M = serde_json::Value> {
#[serde(skip_serializing_if = "String::is_empty", default)]
pub id: String,
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
pub links: HashMap<String, Link>,
pub status: String,
#[serde(skip_serializing_if = "String::is_empty", default)]
pub title: String,
#[serde(skip_serializing_if = "String::is_empty", default)]
pub detail: String,
pub source: Source,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub meta: Option<M>,
}
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
pub struct Source {
#[serde(skip_serializing_if = "String::is_empty", default)]
pub pointer: String,
#[serde(skip_serializing_if = "String::is_empty", default)]
pub parameter: String,
#[serde(skip_serializing_if = "String::is_empty", default)]
pub header: String,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub enum Link<M = DefaultMeta> {
String(String),
Link {
href: String,
#[serde(skip_serializing_if = "String::is_empty", default)]
rel: String,
#[serde(skip_serializing_if = "String::is_empty", default)]
title: String,
#[serde(rename = "type")]
typ: String,
#[serde(skip_serializing_if = "String::is_empty", default)]
hreflang: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
meta: Option<M>,
},
}
impl JsonResponse<(), serde_json::Value> {
pub fn no_data(message: impl ToString) -> Self {
Self {
status_code: 204,
meta: Some(serde_json::json! {{
"message": message.to_string(),
}}),
..Default::default()
}
}
}
impl<D> JsonResponse<D> {
pub fn data(data: D) -> Self {
Self {
status_code: 200,
data: Some(data),
..Default::default()
}
}
}
impl<D: serde::Serialize, M: serde::Serialize> IntoResponse for JsonResponse<D, M> {
fn into_response(self) -> axum::response::Response {
let status = self.status_code;
let mut response = Json(self).into_response();
if status >= 100 {
*response.status_mut() = StatusCode::from_u16(status).unwrap_or_default();
}
response
}
}
impl<D, M> JsonResponse<D, M> {
pub fn error(status: u16, title: impl ToString, detail: impl ToString) -> Self {
let mut me = Self {
status_code: status,
meta: None,
data: None,
..Default::default()
};
me.add_error(ErrorObject {
status: status.to_string(),
title: title.to_string(),
detail: detail.to_string(),
..Default::default()
});
me
}
pub fn add_error(&mut self, error: ErrorObject) -> &mut Self {
self.errors.push(error);
self
}
}
impl<D, M> Default for JsonResponse<D, M> {
fn default() -> Self {
Self {
data: Default::default(),
errors: Default::default(),
meta: Default::default(),
status_code: Default::default(),
}
}
}