1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::fmt::{Display, Formatter};
4use std::sync::OnceLock;
5
6use axum::{
7 http::StatusCode,
8 response::{IntoResponse, Response},
9};
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14#[serde(untagged)]
15pub enum ParameterValue {
16 String(String),
17 Integer(i64),
18 Float(f64),
19 Boolean(bool),
20 Array(Vec<ParameterValue>),
21 Object(HashMap<String, ParameterValue>),
22 Null,
23}
24
25impl From<String> for ParameterValue {
26 fn from(value: String) -> Self {
27 ParameterValue::String(value)
28 }
29}
30
31impl From<&str> for ParameterValue {
32 fn from(value: &str) -> Self {
33 ParameterValue::String(value.to_string())
34 }
35}
36
37impl From<i32> for ParameterValue {
38 fn from(value: i32) -> Self {
39 ParameterValue::Integer(value as i64)
40 }
41}
42
43impl From<i64> for ParameterValue {
44 fn from(value: i64) -> Self {
45 ParameterValue::Integer(value)
46 }
47}
48
49impl From<f32> for ParameterValue {
50 fn from(value: f32) -> Self {
51 ParameterValue::Float(value as f64)
52 }
53}
54
55impl From<f64> for ParameterValue {
56 fn from(value: f64) -> Self {
57 ParameterValue::Float(value)
58 }
59}
60
61impl From<bool> for ParameterValue {
62 fn from(value: bool) -> Self {
63 ParameterValue::Boolean(value)
64 }
65}
66
67impl From<Vec<ParameterValue>> for ParameterValue {
68 fn from(value: Vec<ParameterValue>) -> Self {
69 ParameterValue::Array(value)
70 }
71}
72
73impl From<HashMap<String, ParameterValue>> for ParameterValue {
74 fn from(value: HashMap<String, ParameterValue>) -> Self {
75 ParameterValue::Object(value)
76 }
77}
78
79impl From<Vec<String>> for ParameterValue {
80 fn from(value: Vec<String>) -> Self {
81 ParameterValue::Array(value.into_iter().map(|v| v.into()).collect())
82 }
83}
84
85impl From<Vec<&str>> for ParameterValue {
86 fn from(value: Vec<&str>) -> Self {
87 ParameterValue::Array(value.into_iter().map(|v| v.into()).collect())
88 }
89}
90
91impl From<Vec<i32>> for ParameterValue {
92 fn from(value: Vec<i32>) -> Self {
93 ParameterValue::Array(value.into_iter().map(|v| v.into()).collect())
94 }
95}
96
97impl From<Vec<i64>> for ParameterValue {
98 fn from(value: Vec<i64>) -> Self {
99 ParameterValue::Array(value.into_iter().map(|v| v.into()).collect())
100 }
101}
102
103impl From<Vec<f64>> for ParameterValue {
104 fn from(value: Vec<f64>) -> Self {
105 ParameterValue::Array(value.into_iter().map(|v| v.into()).collect())
106 }
107}
108
109impl From<Vec<bool>> for ParameterValue {
110 fn from(value: Vec<bool>) -> Self {
111 ParameterValue::Array(value.into_iter().map(|v| v.into()).collect())
112 }
113}
114
115impl<T, const N: usize> From<[T; N]> for ParameterValue
117where
118 T: Into<ParameterValue>,
119{
120 fn from(value: [T; N]) -> Self {
121 ParameterValue::Array(value.into_iter().map(|v| v.into()).collect())
122 }
123}
124
125impl<T> From<&[T]> for ParameterValue
126where
127 T: Clone + Into<ParameterValue>,
128{
129 fn from(value: &[T]) -> Self {
130 ParameterValue::Array(value.iter().cloned().map(|v| v.into()).collect())
131 }
132}
133
134impl<K, V, const N: usize> From<[(K, V); N]> for ParameterValue
136where
137 K: Into<String>,
138 V: Into<ParameterValue>,
139{
140 fn from(value: [(K, V); N]) -> Self {
141 let map = value.into_iter()
142 .map(|(k, v)| (k.into(), v.into()))
143 .collect();
144 ParameterValue::Object(map)
145 }
146}
147
148impl<K, V> From<&[(K, V)]> for ParameterValue
149where
150 K: Clone + Into<String>,
151 V: Clone + Into<ParameterValue>,
152{
153 fn from(value: &[(K, V)]) -> Self {
154 let map = value.iter()
155 .map(|(k, v)| (k.clone().into(), v.clone().into()))
156 .collect();
157 ParameterValue::Object(map)
158 }
159}
160
161impl<K, V> From<Vec<(K, V)>> for ParameterValue
162where
163 K: Into<String>,
164 V: Into<ParameterValue>,
165{
166 fn from(value: Vec<(K, V)>) -> Self {
167 let map = value.into_iter()
168 .map(|(k, v)| (k.into(), v.into()))
169 .collect();
170 ParameterValue::Object(map)
171 }
172}
173
174
175impl ParameterValue {
177 pub fn object_from<I>(items: I) -> Self
179 where
180 I: IntoIterator<Item = (String, ParameterValue)>,
181 {
182 ParameterValue::Object(items.into_iter().collect())
183 }
184
185 pub fn object_builder() -> ObjectBuilder {
187 ObjectBuilder::new()
188 }
189
190 pub fn array_builder() -> ArrayBuilder {
192 ArrayBuilder::new()
193 }
194
195 pub fn build_object<F>(f: F) -> Self
197 where
198 F: FnOnce(&mut ObjectBuilder) -> &mut ObjectBuilder,
199 {
200 let mut builder = ObjectBuilder::new();
201 f(&mut builder);
202 builder.build()
203 }
204
205 pub fn build_array<F>(f: F) -> Self
207 where
208 F: FnOnce(&mut ArrayBuilder) -> &mut ArrayBuilder,
209 {
210 let mut builder = ArrayBuilder::new();
211 f(&mut builder);
212 builder.build()
213 }
214}
215
216pub struct ObjectBuilder {
218 map: HashMap<String, ParameterValue>,
219}
220
221impl ObjectBuilder {
222 pub fn new() -> Self {
223 Self {
224 map: HashMap::new(),
225 }
226 }
227
228 pub fn field(mut self, key: impl Into<String>, value: impl Into<ParameterValue>) -> Self {
229 self.map.insert(key.into(), value.into());
230 self
231 }
232
233 pub fn field_mut(&mut self, key: impl Into<String>, value: impl Into<ParameterValue>) -> &mut Self {
234 self.map.insert(key.into(), value.into());
235 self
236 }
237
238 pub fn build(self) -> ParameterValue {
239 ParameterValue::Object(self.map)
240 }
241}
242
243pub struct ArrayBuilder {
245 items: Vec<ParameterValue>,
246}
247
248impl ArrayBuilder {
249 pub fn new() -> Self {
250 Self {
251 items: Vec::new(),
252 }
253 }
254
255 pub fn push(mut self, value: impl Into<ParameterValue>) -> Self {
256 self.items.push(value.into());
257 self
258 }
259
260 pub fn push_mut(&mut self, value: impl Into<ParameterValue>) -> &mut Self {
261 self.items.push(value.into());
262 self
263 }
264
265 pub fn build(self) -> ParameterValue {
266 ParameterValue::Array(self.items)
267 }
268}
269
270#[macro_export]
272macro_rules! param_object {
273 ($($key:expr => $value:expr),* $(,)?) => {
274 $crate::ParameterValue::object_from([
275 $(($key.to_string(), $crate::ParameterValue::from($value))),*
276 ])
277 };
278}
279
280#[macro_export]
282macro_rules! param_array {
283 ($($value:expr),* $(,)?) => {
284 $crate::ParameterValue::Array(vec![
285 $($crate::ParameterValue::from($value)),*
286 ])
287 };
288}
289
290impl Display for ParameterValue {
291 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
292 match self {
293 ParameterValue::String(s) => write!(f, "{}", s),
294 ParameterValue::Integer(i) => write!(f, "{}", i),
295 ParameterValue::Float(float) => write!(f, "{}", float),
296 ParameterValue::Boolean(b) => write!(f, "{}", b),
297 ParameterValue::Array(arr) => {
298 write!(f, "[")?;
299 for (i, item) in arr.iter().enumerate() {
300 if i > 0 {
301 write!(f, ", ")?;
302 }
303 write!(f, "{}", item)?;
304 }
305 write!(f, "]")
306 }
307 ParameterValue::Object(obj) => {
308 write!(f, "{{")?;
309 for (i, (key, value)) in obj.iter().enumerate() {
310 if i > 0 {
311 write!(f, ", ")?;
312 }
313 write!(f, "{}: {}", key, value)?;
314 }
315 write!(f, "}}")
316 }
317 ParameterValue::Null => write!(f, "null"),
318 }
319 }
320}
321
322impl ParameterValue {
323 pub fn array(items: Vec<impl Into<ParameterValue>>) -> Self {
325 ParameterValue::Array(items.into_iter().map(|v| v.into()).collect())
326 }
327
328 pub fn object(map: impl Into<HashMap<String, ParameterValue>>) -> Self {
330 ParameterValue::Object(map.into())
331 }
332}
333
334pub trait ResponseBuilder: std::fmt::Debug + Send + Sync {
336 fn build(&self, error: &ServiceError) -> (String, &'static str);
338}
339
340static DEFAULT_RESPONSE_BUILDER: OnceLock<Box<dyn ResponseBuilder>> = OnceLock::new();
342
343pub fn set_default_response_builder(builder: impl ResponseBuilder + 'static) {
346 DEFAULT_RESPONSE_BUILDER.set(Box::new(builder)).ok();
347}
348
349fn get_default_response_builder() -> Option<&'static Box<dyn ResponseBuilder>> {
351 DEFAULT_RESPONSE_BUILDER.get()
352}
353
354#[derive(Debug, Serialize, Deserialize)]
356pub struct ServiceError<'a> {
357 pub code: u32,
360 #[serde(borrow)]
362 pub name: Cow<'a, str>,
363 #[serde(skip)]
365 pub http_status: u16,
366 #[serde(borrow)]
368 pub message: Cow<'a, str>,
369 #[serde(skip)]
371 pub arguments: Vec<String>,
372 #[serde(skip_serializing_if = "Option::is_none")]
374 pub parameters: Option<HashMap<String, ParameterValue>>,
375 #[serde(skip)]
377 response_builder: Option<Box<dyn ResponseBuilder>>,
378}
379
380impl<'a> Clone for ServiceError<'a> {
381 fn clone(&self) -> Self {
382 Self {
383 code: self.code,
384 name: self.name.clone(),
385 http_status: self.http_status,
386 message: self.message.clone(),
387 arguments: self.arguments.clone(),
388 parameters: self.parameters.clone(),
389 response_builder: None, }
391 }
392}
393
394impl<'a> ServiceError<'a> {
395 pub const fn new(code: u32, name: &'a str, status: u16, message: &'a str) -> Self {
397 Self {
398 code,
399 name: Cow::Borrowed(name),
400 http_status: status,
401 message: Cow::Borrowed(message),
402 arguments: Vec::new(),
403 parameters: None,
404 response_builder: None,
405 }
406 }
407
408 pub fn bind(mut self, value: impl ToString) -> Self {
410 self.arguments.push(value.to_string());
411 self
412 }
413
414 pub fn parameter(mut self, key: impl ToString, value: impl Into<ParameterValue>) -> Self {
416 let parameters = self.parameters.get_or_insert_with(HashMap::new);
417 parameters.insert(key.to_string(), value.into());
418 self
419 }
420
421 pub fn parameters<K, V, I>(mut self, params: I) -> Self
423 where
424 K: Into<String>,
425 V: Into<ParameterValue>,
426 I: IntoIterator<Item = (K, V)>,
427 {
428 let parameters = self.parameters.get_or_insert_with(HashMap::new);
429 for (key, value) in params {
430 parameters.insert(key.into(), value.into());
431 }
432 self
433 }
434
435 pub fn with_response_builder(mut self, builder: impl ResponseBuilder + 'static) -> Self {
437 self.response_builder = Some(Box::new(builder));
438 self
439 }
440
441 fn format_message(&self) -> String {
443 let mut formatted = self.message.to_string();
444 for (i, arg) in self.arguments.iter().enumerate() {
445 let placeholder = format!("{{{i}}}");
446 formatted = formatted.replace(&placeholder, arg);
447 }
448 formatted
449 }
450}
451
452impl<'a> IntoResponse for ServiceError<'a> {
453 fn into_response(self) -> Response {
454 let status_code =
455 StatusCode::from_u16(self.http_status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
456
457 let (body, content_type) = if let Some(builder) = &self.response_builder {
458 builder.build(&self)
460 } else if let Some(default_builder) = get_default_response_builder() {
461 default_builder.build(&self)
463 } else {
464 let text = if let Some(ref params) = self.parameters {
466 let param_display: Vec<String> = params
467 .iter()
468 .map(|(k, v)| format!("{}: {}", k, v))
469 .collect();
470 format!(
471 "Error {}: {} - {} (Parameters: {{{}}})",
472 self.code,
473 self.name,
474 self.format_message(),
475 param_display.join(", ")
476 )
477 } else {
478 format!(
479 "Error {}: {} - {}",
480 self.code,
481 self.name,
482 self.format_message()
483 )
484 };
485 (text, "text/plain")
486 };
487
488 (status_code, [("content-type", content_type)], body).into_response()
489 }
490}
491
492#[cfg(feature = "json")]
494#[derive(Debug, Clone)]
495pub struct JsonResponseBuilder;
496
497#[cfg(feature = "json")]
498impl JsonResponseBuilder {
499 pub fn new() -> Self {
500 Self
501 }
502}
503
504#[cfg(feature = "json")]
505impl ResponseBuilder for JsonResponseBuilder {
506 fn build(&self, error: &ServiceError) -> (String, &'static str) {
507 let response_body = JsonResponseBody {
508 code: error.code,
509 name: error.name.clone(),
510 message: error.format_message(),
511 parameters: error.parameters.clone(),
512 };
513
514 let json = serde_json::to_string(&response_body).unwrap_or_else(|_| {
515 format!("{{\"error\":\"Failed to serialize error {}\"}}", error.code)
516 });
517
518 (json, "application/json")
519 }
520}
521
522#[cfg(feature = "json")]
523#[derive(Debug, Clone, Serialize)]
524struct JsonResponseBody<'a> {
525 code: u32,
526 #[serde(borrow)]
527 name: Cow<'a, str>,
528 message: String,
529 #[serde(skip_serializing_if = "Option::is_none")]
530 parameters: Option<HashMap<String, ParameterValue>>,
531}
532
533#[derive(Debug, Clone)]
535pub struct PlainTextResponseBuilder;
536
537impl Default for PlainTextResponseBuilder {
538 fn default() -> Self {
539 Self::new()
540 }
541}
542
543impl PlainTextResponseBuilder {
544 pub fn new() -> Self {
545 Self
546 }
547}
548
549impl ResponseBuilder for PlainTextResponseBuilder {
550 fn build(&self, error: &ServiceError) -> (String, &'static str) {
551 let text = if let Some(ref params) = error.parameters {
552 let param_display: Vec<String> = params
553 .iter()
554 .map(|(k, v)| format!("{}: {}", k, v))
555 .collect();
556 format!(
557 "Error {}: {} - {} (Parameters: {{{}}})",
558 error.code,
559 error.name,
560 error.format_message(),
561 param_display.join(", ")
562 )
563 } else {
564 format!(
565 "Error {}: {} - {}",
566 error.code,
567 error.name,
568 error.format_message()
569 )
570 };
571 (text, "text/plain")
572 }
573}