use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub use reinhardt_core::pagination::PaginatedResponse;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiResponse<T> {
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<T>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub errors: Option<HashMap<String, Vec<String>>>,
pub status: u16,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
impl<T> ApiResponse<T> {
pub fn success(data: T) -> Self {
Self {
data: Some(data),
error: None,
errors: None,
status: 200,
message: None,
}
}
pub fn success_with_status(data: T, status: u16) -> Self {
Self {
data: Some(data),
error: None,
errors: None,
status,
message: None,
}
}
pub fn error(message: impl Into<String>, status: u16) -> Self {
Self {
data: None,
error: Some(message.into()),
errors: None,
status,
message: None,
}
}
pub fn validation_error(errors: HashMap<String, Vec<String>>) -> Self {
Self {
data: None,
error: Some("Validation failed".to_string()),
errors: Some(errors),
status: 400,
message: None,
}
}
pub fn not_found() -> Self {
Self {
data: None,
error: Some("Not found".to_string()),
errors: None,
status: 404,
message: None,
}
}
pub fn unauthorized() -> Self {
Self {
data: None,
error: Some("Unauthorized".to_string()),
errors: None,
status: 401,
message: None,
}
}
pub fn forbidden() -> Self {
Self {
data: None,
error: Some("Forbidden".to_string()),
errors: None,
status: 403,
message: None,
}
}
pub fn with_message(mut self, message: impl Into<String>) -> Self {
self.message = Some(message.into());
self
}
pub fn to_json(&self) -> Result<String, serde_json::Error>
where
T: Serialize,
{
serde_json::to_string(self)
}
pub fn to_json_pretty(&self) -> Result<String, serde_json::Error>
where
T: Serialize,
{
serde_json::to_string_pretty(self)
}
}
pub struct ResponseBuilder<T> {
data: Option<T>,
error: Option<String>,
errors: Option<HashMap<String, Vec<String>>>,
status: u16,
message: Option<String>,
}
impl<T> ResponseBuilder<T> {
pub fn new() -> Self {
Self {
data: None,
error: None,
errors: None,
status: 200,
message: None,
}
}
pub fn data(mut self, data: T) -> Self {
self.data = Some(data);
self
}
pub fn error(mut self, error: impl Into<String>) -> Self {
self.error = Some(error.into());
self
}
pub fn errors(mut self, errors: HashMap<String, Vec<String>>) -> Self {
self.errors = Some(errors);
self
}
pub fn status(mut self, status: u16) -> Self {
self.status = status;
self
}
pub fn message(mut self, message: impl Into<String>) -> Self {
self.message = Some(message.into());
self
}
pub fn build(self) -> ApiResponse<T> {
ApiResponse {
data: self.data,
error: self.error,
errors: self.errors,
status: self.status,
message: self.message,
}
}
}
impl<T> Default for ResponseBuilder<T> {
fn default() -> Self {
Self::new()
}
}
pub trait IntoApiResponse<T> {
fn into_api_response(self) -> ApiResponse<T>;
}
impl<T> IntoApiResponse<T> for Result<T, String> {
fn into_api_response(self) -> ApiResponse<T> {
match self {
Ok(data) => ApiResponse::success(data),
Err(err) => ApiResponse::error(err, 500),
}
}
}
impl<T> IntoApiResponse<T> for Option<T> {
fn into_api_response(self) -> ApiResponse<T> {
match self {
Some(data) => ApiResponse::success(data),
None => ApiResponse::not_found(),
}
}
}