Line data Source code
1 : use axum::{extract::multipart, response::IntoResponse};
2 : use http::StatusCode;
3 : use std::sync::PoisonError;
4 :
5 : /// How to format the error message in the response body
6 : #[derive(Default)]
7 : pub enum Format {
8 : /// Use the error Symbol’s value as variable is void: msg as is
9 : #[default]
10 : Plain,
11 : /// Format the error as a JSON object
12 : Json,
13 : }
14 :
15 : /// A struct representing an HTTP Response in the 4XX-5XX range.
16 : #[derive(Default)]
17 : pub struct ResponseError {
18 : pub status: StatusCode,
19 : pub msg: String,
20 : // TODO: Can we set the format from the content_type after the handler returns?
21 : pub format: Format,
22 : }
23 :
24 : impl ResponseError {
25 3 : pub(crate) fn server_error() -> Self {
26 3 : ResponseError { status: StatusCode::INTERNAL_SERVER_ERROR, ..Default::default() }
27 3 : }
28 1 : pub(crate) fn unauthorized() -> Self {
29 1 : ResponseError { status: StatusCode::UNAUTHORIZED, ..Default::default() }
30 1 : }
31 : }
32 :
33 : impl IntoResponse for ResponseError {
34 2 : fn into_response(self) -> axum::response::Response {
35 2 : match self.format {
36 2 : Format::Plain => (self.status, self.msg).into_response(),
37 0 : Format::Json => (self.status, format!("{{\"msg\": \"{}\"}}", self.msg)).into_response(),
38 : }
39 2 : }
40 : }
41 :
42 : impl From<minijinja::Error> for ResponseError {
43 0 : fn from(value: minijinja::Error) -> Self {
44 0 : ResponseError {
45 0 : status: StatusCode::INTERNAL_SERVER_ERROR,
46 0 : msg: value.to_string(),
47 0 : format: Format::Plain,
48 0 : }
49 0 : }
50 : }
51 :
52 : impl From<rusqlite::Error> for ResponseError {
53 1 : fn from(value: rusqlite::Error) -> Self {
54 1 : let status = if value == rusqlite::Error::QueryReturnedNoRows {
55 1 : StatusCode::NOT_FOUND
56 : } else {
57 0 : StatusCode::INTERNAL_SERVER_ERROR
58 : };
59 :
60 1 : ResponseError { status, msg: value.to_string(), format: Format::Plain }
61 1 : }
62 : }
63 :
64 : impl From<anyhow::Error> for ResponseError {
65 0 : fn from(value: anyhow::Error) -> Self {
66 0 : ResponseError {
67 0 : status: StatusCode::INTERNAL_SERVER_ERROR,
68 0 : msg: value.to_string(),
69 0 : format: Format::Plain,
70 0 : }
71 0 : }
72 : }
73 :
74 : impl From<http::Error> for ResponseError {
75 0 : fn from(value: http::Error) -> Self {
76 0 : ResponseError {
77 0 : status: StatusCode::INTERNAL_SERVER_ERROR,
78 0 : msg: value.to_string(),
79 0 : format: Format::Plain,
80 0 : }
81 0 : }
82 : }
83 :
84 : impl From<multipart::MultipartError> for ResponseError {
85 0 : fn from(value: multipart::MultipartError) -> Self {
86 0 : ResponseError {
87 0 : status: StatusCode::INTERNAL_SERVER_ERROR,
88 0 : msg: value.to_string(),
89 0 : format: Format::Plain,
90 0 : }
91 0 : }
92 : }
93 :
94 : impl From<std::io::Error> for ResponseError {
95 0 : fn from(value: std::io::Error) -> Self {
96 0 : ResponseError {
97 0 : status: StatusCode::INTERNAL_SERVER_ERROR,
98 0 : msg: value.to_string(),
99 0 : format: Format::Plain,
100 0 : }
101 0 : }
102 : }
103 :
104 : impl<T> From<PoisonError<T>> for ResponseError {
105 0 : fn from(_: PoisonError<T>) -> Self {
106 0 : ResponseError {
107 0 : status: StatusCode::INTERNAL_SERVER_ERROR,
108 0 : msg: "LockFailure".to_string(),
109 0 : format: Format::Plain,
110 0 : }
111 0 : }
112 : }
|