1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
use axum::{
  body::Body,
  extract::{rejection::JsonRejection, FromRequest},
  http::{header::IntoHeaderName, HeaderMap, HeaderValue, StatusCode},
  response::{IntoResponse, Response},
};
use serde::Serialize;

use crate::serialize_error;

pub type Result<T> = std::result::Result<T, Error>;

/// Intermediate error type which can be converted to from any error using `?`.
/// The standard `impl From<E> for Error` will attach StatusCode::INTERNAL_SERVER_ERROR,
/// so if an alternative StatusCode is desired, you should use `.status_code` ([AddStatusCode] or [AddStatusCodeError])
/// to add the status and `.header` ([AddHeader] or [AddHeaderError]) before using `?`.
pub struct Error {
  pub status: StatusCode,
  pub headers: HeaderMap,
  pub error: anyhow::Error,
}

impl Error {
  pub fn status_code(mut self, status_code: StatusCode) -> Error {
    self.status = status_code;
    self
  }

  pub fn header(mut self, name: impl IntoHeaderName, value: HeaderValue) -> Error {
    self.headers.append(name, value);
    self
  }

  pub fn headers(mut self, headers: HeaderMap) -> Error {
    self.headers = headers;
    self
  }
}

impl IntoResponse for Error {
  fn into_response(self) -> Response {
    let mut response = Response::new(Body::new(serialize_error(&self.error)));
    *response.status_mut() = self.status;

    let headers = response.headers_mut();
    headers.append("Content-Type", HeaderValue::from_static("application/json"));
    headers.extend(self.headers);

    response
  }
}

impl<E> From<E> for Error
where
  E: Into<anyhow::Error>,
{
  fn from(err: E) -> Self {
    Self {
      status: StatusCode::INTERNAL_SERVER_ERROR,
      headers: Default::default(),
      error: err.into(),
    }
  }
}

/// Convenience trait to convert any Error into serror::Error by adding status
/// and converting error into anyhow error.
pub trait AddStatusCodeError: Into<anyhow::Error> {
  fn status_code(self, status_code: StatusCode) -> Error {
    Error {
      status: status_code,
      headers: Default::default(),
      error: self.into(),
    }
  }
}

impl<E> AddStatusCodeError for E where E: Into<anyhow::Error> {}

/// Convenience trait to convert Result into serror::Result by adding status to the inner error, if it exists.
pub trait AddStatusCode<T, E>: Into<std::result::Result<T, E>>
where
  E: Into<anyhow::Error>,
{
  fn status_code(self, status_code: StatusCode) -> Result<T> {
    self.into().map_err(|e| e.status_code(status_code))
  }
}

impl<R, T, E> AddStatusCode<T, E> for R
where
  R: Into<std::result::Result<T, E>>,
  E: Into<anyhow::Error>,
{
}

/// Convenience trait to convert any Error into serror::Error by adding headers
/// and converting error into anyhow error.
pub trait AddHeadersError: Into<anyhow::Error> {
  fn header(self, name: impl IntoHeaderName, value: HeaderValue) -> Error {
    let mut headers = HeaderMap::with_capacity(1);
    headers.append(name, value);
    Error {
      headers,
      status: StatusCode::INTERNAL_SERVER_ERROR,
      error: self.into(),
    }
  }
  fn headers(self, headers: HeaderMap) -> Error {
    Error {
      headers,
      status: StatusCode::INTERNAL_SERVER_ERROR,
      error: self.into(),
    }
  }
}

impl<E> AddHeadersError for E where E: Into<anyhow::Error> {}

/// Convenience trait to add headers to a serror::Result directly.
pub trait AddHeaders<T, E>: Into<std::result::Result<T, E>>
where
  E: Into<anyhow::Error>,
{
  fn header(self, name: impl IntoHeaderName, value: HeaderValue) -> Result<T> {
    self.into().map_err(|e| e.header(name, value))
  }

  /// Some headers might want to be attached in both Ok case and Err case.
  /// Borrow headers here so they can be used later, as they will only be cloned in err case.
  fn headers(self, headers: &HeaderMap) -> Result<T> {
    self.into().map_err(|e| e.headers(headers.clone()))
  }
}

impl<R, T, E> AddHeaders<T, E> for R
where
  R: Into<std::result::Result<T, E>>,
  E: Into<anyhow::Error>,
{
}

/// Wrapper for axum::Json that converts parsing error to serror::Error
#[derive(FromRequest)]
#[from_request(via(axum::Json), rejection(JsonError))]
pub struct Json<T>(pub T);

impl<T: Serialize> IntoResponse for Json<T> {
  fn into_response(self) -> Response {
    axum::Json(self.0).into_response()
  }
}

pub struct JsonError(Error);

/// Convert the JsonRejection into JsonError(serror::Error)
impl From<JsonRejection> for JsonError {
  fn from(rejection: JsonRejection) -> Self {
    Self(Error {
      status: rejection.status(),
      headers: Default::default(),
      error: rejection.into(),
    })
  }
}

impl IntoResponse for JsonError {
  fn into_response(self) -> Response {
    self.0.into_response()
  }
}