dutils/
error.rs

1use anyhow::anyhow;
2use http::StatusCode;
3use std::fmt::Display;
4
5pub trait ContextWrapper<T, E> {
6    fn anyhow(self) -> Result<T, anyhow::Error>;
7    fn anyhow_with(self, msg: impl AsRef<str>) -> Result<T, anyhow::Error>;
8
9    fn track(self) -> Self;
10    fn track_with(self, msg: impl AsRef<str>) -> Self;
11}
12
13pub trait ErrToMsg {
14    fn to_msg(self, loc: &std::panic::Location) -> String;
15    fn to_msg_with(self, msg: impl AsRef<str>, loc: &std::panic::Location) -> String;
16}
17
18impl<E: Display> ErrToMsg for E {
19    #[track_caller]
20    fn to_msg(self, loc: &std::panic::Location) -> String {
21        let e = self.to_string();
22
23        if e.is_empty() {
24            format!("\n- [{}:{}]", loc.file(), loc.line())
25        } else {
26            format!("\n- [{}:{}] {e}", loc.file(), loc.line())
27        }
28    }
29
30    #[track_caller]
31    fn to_msg_with(self, msg: impl AsRef<str>, loc: &std::panic::Location) -> String {
32        let e = self.to_string();
33        if e.is_empty() {
34            format!("\n- [{}:{}] {}", loc.file(), loc.line(), msg.as_ref())
35        } else {
36            format!("\n- [{}:{}] {} {e}", loc.file(), loc.line(), msg.as_ref())
37        }
38    }
39}
40
41impl<T, E: Display> ContextWrapper<T, E> for Result<T, E> {
42    #[track_caller]
43    fn anyhow(self) -> Result<T, anyhow::Error> {
44        match self {
45            Ok(v) => Ok(v),
46            Err(e) => Err(anyhow!(e.to_msg(std::panic::Location::caller()))),
47        }
48    }
49
50    #[track_caller]
51    fn anyhow_with(self, msg: impl AsRef<str>) -> Result<T, anyhow::Error> {
52        match self {
53            Ok(v) => Ok(v),
54            Err(e) => Err(anyhow!(e.to_msg_with(msg, std::panic::Location::caller()))),
55        }
56    }
57
58    #[track_caller]
59    fn track(self) -> Self {
60        if let Err(e) = &self {
61            tracing::error!(target: "dutils", "{}", e.to_msg(std::panic::Location::caller()));
62        }
63        self
64    }
65    #[track_caller]
66    fn track_with(self, msg: impl AsRef<str>) -> Self {
67        if let Err(e) = &self {
68            tracing::error!(target: "dutils", "{}", e.to_msg_with(msg, std::panic::Location::caller()));
69        }
70        self
71    }
72}
73
74impl<T> ContextWrapper<T, ()> for Option<T> {
75    #[track_caller]
76    fn anyhow(self) -> Result<T, anyhow::Error> {
77        match self {
78            Some(v) => Ok(v),
79            None => Err(anyhow!(
80                "Value is None".to_msg(std::panic::Location::caller())
81            )),
82        }
83    }
84
85    #[track_caller]
86    fn anyhow_with(self, msg: impl AsRef<str>) -> Result<T, anyhow::Error> {
87        match self {
88            Some(v) => Ok(v),
89            None => Err(anyhow!(
90                "Value is None".to_msg_with(msg, std::panic::Location::caller())
91            )),
92        }
93    }
94
95    #[track_caller]
96    fn track(self) -> Self {
97        if self.is_none() {
98            tracing::error!(target: "dutils", "{}", "Value is None".to_msg(std::panic::Location::caller()));
99        }
100        self
101    }
102    #[track_caller]
103    fn track_with(self, msg: impl AsRef<str>) -> Self {
104        if self.is_none() {
105            tracing::error!(target: "dutils", "{}", "Value is None".to_msg_with(msg, std::panic::Location::caller()));
106        }
107        self
108    }
109}
110
111fn api_err_response(status: StatusCode, msg: impl AsRef<str>) -> http::Response<String> {
112    http::Response::builder()
113        .status(status)
114        .body(serde_json::to_string(msg.as_ref()).unwrap())
115        .unwrap()
116}
117
118pub trait ApiError {
119    type Ok;
120    fn bad_request_from_error(self) -> core::result::Result<Self::Ok, http::Response<String>>;
121    fn bad_request(
122        self,
123        msg: impl AsRef<str>,
124    ) -> core::result::Result<Self::Ok, http::Response<String>>;
125    fn not_found(
126        self,
127        msg: impl AsRef<str>,
128    ) -> core::result::Result<Self::Ok, http::Response<String>>;
129    fn internal(
130        self,
131        msg: impl AsRef<str>,
132    ) -> core::result::Result<Self::Ok, http::Response<String>>;
133
134    fn internal_from_error(self) -> core::result::Result<Self::Ok, http::Response<String>>;
135}
136
137pub trait ApiErrorConflict {
138    type Ok;
139    fn conflict(
140        self,
141        msg: impl AsRef<str>,
142    ) -> core::result::Result<Self::Ok, http::Response<String>>;
143}
144
145impl ApiError for String {
146    type Ok = ();
147
148    fn bad_request_from_error(self) -> core::result::Result<Self::Ok, http::Response<String>> {
149        Err(api_err_response(StatusCode::BAD_REQUEST, self))
150    }
151
152    fn bad_request(
153        self,
154        msg: impl AsRef<str>,
155    ) -> core::result::Result<Self::Ok, http::Response<String>> {
156        Err(api_err_response(StatusCode::BAD_REQUEST, msg))
157    }
158
159    fn not_found(
160        self,
161        msg: impl AsRef<str>,
162    ) -> core::result::Result<Self::Ok, http::Response<String>> {
163        Err(api_err_response(StatusCode::NOT_FOUND, msg))
164    }
165
166    fn internal(
167        self,
168        msg: impl AsRef<str>,
169    ) -> core::result::Result<Self::Ok, http::Response<String>> {
170        Err(api_err_response(StatusCode::INTERNAL_SERVER_ERROR, msg))
171    }
172
173    fn internal_from_error(self) -> core::result::Result<Self::Ok, http::Response<String>> {
174        Err(api_err_response(StatusCode::INTERNAL_SERVER_ERROR, self))
175    }
176}
177
178impl ApiError for &'_ str {
179    type Ok = ();
180
181    fn bad_request_from_error(self) -> core::result::Result<Self::Ok, http::Response<String>> {
182        Err(api_err_response(StatusCode::BAD_REQUEST, self))
183    }
184
185    fn bad_request(
186        self,
187        msg: impl AsRef<str>,
188    ) -> core::result::Result<Self::Ok, http::Response<String>> {
189        Err(api_err_response(StatusCode::BAD_REQUEST, msg))
190    }
191
192    fn not_found(
193        self,
194        msg: impl AsRef<str>,
195    ) -> core::result::Result<Self::Ok, http::Response<String>> {
196        Err(api_err_response(StatusCode::NOT_FOUND, msg))
197    }
198
199    fn internal(
200        self,
201        msg: impl AsRef<str>,
202    ) -> core::result::Result<Self::Ok, http::Response<String>> {
203        Err(api_err_response(StatusCode::INTERNAL_SERVER_ERROR, msg))
204    }
205
206    fn internal_from_error(self) -> core::result::Result<Self::Ok, http::Response<String>> {
207        Err(api_err_response(StatusCode::INTERNAL_SERVER_ERROR, self))
208    }
209}
210
211impl<T, E: Display> ApiError for core::result::Result<T, E> {
212    type Ok = T;
213
214    fn bad_request_from_error(self) -> core::result::Result<T, http::Response<String>> {
215        match self {
216            Ok(a) => Ok(a),
217            Err(e) => Err(api_err_response(StatusCode::BAD_REQUEST, e.to_string())),
218        }
219    }
220
221    fn bad_request(self, msg: impl AsRef<str>) -> core::result::Result<T, http::Response<String>> {
222        match self {
223            Ok(a) => Ok(a),
224            Err(_) => Err(api_err_response(StatusCode::BAD_REQUEST, msg)),
225        }
226    }
227
228    fn not_found(self, msg: impl AsRef<str>) -> core::result::Result<T, http::Response<String>> {
229        match self {
230            Ok(a) => Ok(a),
231            Err(_) => Err(api_err_response(StatusCode::NOT_FOUND, msg)),
232        }
233    }
234
235    fn internal(self, msg: impl AsRef<str>) -> core::result::Result<T, http::Response<String>> {
236        match self {
237            Ok(a) => Ok(a),
238            Err(_) => Err(api_err_response(StatusCode::INTERNAL_SERVER_ERROR, msg)),
239        }
240    }
241
242    fn internal_from_error(self) -> core::result::Result<T, http::Response<String>> {
243        match self {
244            Ok(a) => Ok(a),
245            Err(e) => Err(api_err_response(
246                StatusCode::INTERNAL_SERVER_ERROR,
247                e.to_string(),
248            )),
249        }
250    }
251}
252
253impl<T> ApiError for Option<T> {
254    type Ok = T;
255
256    fn bad_request_from_error(self) -> core::result::Result<T, http::Response<String>> {
257        match self {
258            Some(a) => Ok(a),
259            None => Err(api_err_response(StatusCode::BAD_REQUEST, "No value")),
260        }
261    }
262
263    fn bad_request(self, msg: impl AsRef<str>) -> core::result::Result<T, http::Response<String>> {
264        match self {
265            Some(a) => Ok(a),
266            None => Err(api_err_response(StatusCode::BAD_REQUEST, msg)),
267        }
268    }
269
270    fn not_found(self, msg: impl AsRef<str>) -> core::result::Result<T, http::Response<String>> {
271        match self {
272            Some(a) => Ok(a),
273            None => Err(api_err_response(StatusCode::NOT_FOUND, msg)),
274        }
275    }
276
277    fn internal(self, msg: impl AsRef<str>) -> core::result::Result<T, http::Response<String>> {
278        match self {
279            Some(a) => Ok(a),
280            None => Err(api_err_response(StatusCode::INTERNAL_SERVER_ERROR, msg)),
281        }
282    }
283
284    fn internal_from_error(self) -> core::result::Result<T, http::Response<String>> {
285        match self {
286            Some(a) => Ok(a),
287            None => Err(api_err_response(
288                StatusCode::INTERNAL_SERVER_ERROR,
289                "No value",
290            )),
291        }
292    }
293}
294
295impl<T> ApiErrorConflict for Option<T> {
296    type Ok = ();
297
298    fn conflict(self, msg: impl AsRef<str>) -> core::result::Result<(), http::Response<String>> {
299        match self {
300            None => Ok(()),
301            Some(_) => Err(api_err_response(StatusCode::CONFLICT, msg)),
302        }
303    }
304}
305
306pub trait ToResult<T> {
307    fn to_result(self) -> Result<T, anyhow::Error>;
308}
309
310impl<T> ToResult<T> for T {
311    fn to_result(self) -> Result<T, anyhow::Error> {
312        Ok(self)
313    }
314}