fraiseql_server/routes/api/
types.rs1use std::fmt;
4
5use axum::{
6 Json,
7 http::StatusCode,
8 response::{IntoResponse, Response},
9};
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Serialize, Deserialize, Clone)]
14pub struct ApiError {
15 pub error: String,
17 pub code: String,
19 pub details: Option<String>,
21}
22
23impl ApiError {
24 pub fn new(error: impl Into<String>, code: impl Into<String>) -> Self {
26 Self {
27 error: error.into(),
28 code: code.into(),
29 details: None,
30 }
31 }
32
33 pub fn with_details(mut self, details: impl Into<String>) -> Self {
35 self.details = Some(details.into());
36 self
37 }
38
39 pub fn parse_error(msg: impl fmt::Display) -> Self {
41 Self::new(format!("Parse error: {}", msg), "PARSE_ERROR")
42 }
43
44 pub fn validation_error(msg: impl fmt::Display) -> Self {
46 Self::new(format!("Validation error: {}", msg), "VALIDATION_ERROR")
47 }
48
49 pub fn internal_error(msg: impl fmt::Display) -> Self {
51 Self::new(format!("Internal server error: {}", msg), "INTERNAL_ERROR")
52 }
53
54 #[must_use]
56 pub fn unauthorized() -> Self {
57 Self::new("Unauthorized", "UNAUTHORIZED")
58 }
59
60 pub fn not_found(msg: impl fmt::Display) -> Self {
62 Self::new(format!("Not found: {}", msg), "NOT_FOUND")
63 }
64}
65
66impl fmt::Display for ApiError {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 write!(f, "{}: {}", self.code, self.error)
69 }
70}
71
72impl IntoResponse for ApiError {
73 fn into_response(self) -> Response {
74 let status = match self.code.as_str() {
75 "UNAUTHORIZED" => StatusCode::UNAUTHORIZED,
76 "FORBIDDEN" => StatusCode::FORBIDDEN,
77 "NOT_FOUND" => StatusCode::NOT_FOUND,
78 "VALIDATION_ERROR" | "PARSE_ERROR" => StatusCode::BAD_REQUEST,
79 "UNSUPPORTED_OPERATION" => StatusCode::NOT_IMPLEMENTED,
80 "SERVICE_UNAVAILABLE" => StatusCode::SERVICE_UNAVAILABLE,
81 _ => StatusCode::INTERNAL_SERVER_ERROR,
82 };
83
84 (status, Json(self)).into_response()
85 }
86}
87
88#[derive(Debug, Serialize, Deserialize)]
90pub struct ApiResponse<T> {
91 pub status: String,
93 pub data: T,
95}
96
97impl<T: Serialize> ApiResponse<T> {
98 pub fn success(data: T) -> Json<Self> {
100 Json(Self {
101 status: "success".to_string(),
102 data,
103 })
104 }
105}
106
107#[derive(Debug, Serialize, Deserialize, Clone)]
112pub struct SanitizedConfig {
113 pub port: u16,
115
116 pub host: String,
118
119 pub workers: Option<usize>,
121
122 pub tls_enabled: bool,
124
125 pub sanitized: bool,
127}
128
129impl SanitizedConfig {
130 #[must_use]
137 pub fn from_config(config: &crate::config::HttpServerConfig) -> Self {
138 Self {
139 port: config.port,
140 host: config.host.clone(),
141 workers: config.workers,
142 tls_enabled: config.tls.is_some(),
143 sanitized: true,
144 }
145 }
146
147 #[must_use]
149 pub const fn is_sanitized(&self) -> bool {
150 self.sanitized
151 }
152}