1use std::borrow::Cow;
5
6use bytes::{BufMut, BytesMut};
7use http::{header::CONTENT_TYPE, HeaderName, HeaderValue, StatusCode};
8
9#[derive(serde::Serialize, Debug)]
10pub struct ProblemDetails<Extension> {
11 #[serde(rename = "type")]
12 pub type_: Cow<'static, str>,
13 pub status: u16,
14 pub title: Cow<'static, str>,
15 pub detail: Cow<'static, str>,
16 #[serde(flatten)]
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub extensions: Option<Extension>,
19}
20
21#[derive(serde::Serialize)]
22pub struct ValidationErrors {
23 pub errors: Vec<ValidationError>,
24}
25
26#[derive(serde::Serialize)]
27pub struct ValidationError {
28 pub detail: String,
29 #[serde(flatten)]
30 pub source: Source,
31}
32
33#[derive(serde::Serialize)]
35#[serde(tag = "source", rename_all = "snake_case")]
36pub enum Source {
37 Body {
38 pointer: Option<String>,
41 },
42 Header {
43 name: Cow<'static, str>,
45 },
46}
47
48impl<Extension> axum_core::response::IntoResponse for ProblemDetails<Extension>
49where
50 Extension: serde::Serialize,
51{
52 fn into_response(self) -> axum_core::response::Response {
53 let mut buf = BytesMut::with_capacity(128).writer();
56 match serde_json::to_writer(&mut buf, &self) {
57 Ok(()) => (
58 [(CONTENT_TYPE, APPLICATION_PROBLEM_JSON)],
59 buf.into_inner().freeze(),
60 )
61 .into_response(),
62 Err(_) => INTERNAL_SERVER_ERROR.into_response(),
63 }
64 }
65}
66
67pub const APPLICATION_PROBLEM_JSON: HeaderValue =
68 HeaderValue::from_static("application/problem+json");
69
70pub const INTERNAL_SERVER_ERROR: (StatusCode, [(HeaderName, HeaderValue); 1], &[u8]) = (
71 StatusCode::INTERNAL_SERVER_ERROR,
72 [(CONTENT_TYPE, APPLICATION_PROBLEM_JSON)],
73 INTERNAL_SERVER_ERROR_PROBLEM,
74);
75
76pub const INTERNAL_SERVER_ERROR_PROBLEM: &[u8] = br#"{
77 "type": "internal_server_error",
78 "title": "Internal Server Error",
79 "detail": "Something went wrong when processing your request. Please try again later."
80 "status": 500
81}"#;