1use http::StatusCode;
2use std::{borrow::Cow, result::Result as StdResult};
3
4use crate::HttpError;
5
6pub trait ResultExt {
8 type Item;
9
10 fn map_status(self, status_code: StatusCode) -> anyhow::Result<Self::Item>;
25
26 fn map_http_error<S>(self, status_code: StatusCode, reason: S) -> anyhow::Result<Self::Item>
43 where
44 S: Into<Cow<'static, str>>;
45}
46
47impl<E, T> ResultExt for StdResult<T, E>
48where
49 E: Into<anyhow::Error> + Send + Sync + 'static,
50{
51 type Item = T;
52
53 fn map_status(self, status_code: StatusCode) -> anyhow::Result<T> {
54 match self {
55 Ok(v) => Ok(v),
56 Err(e) => Err(HttpError::from_err(e).with_status_code(status_code).into()),
57 }
58 }
59
60 fn map_http_error<S>(self, status_code: StatusCode, reason: S) -> anyhow::Result<Self::Item>
61 where
62 S: Into<Cow<'static, str>>,
63 {
64 match self {
65 Ok(v) => Ok(v),
66 Err(e) => Err(HttpError::from_err(e)
67 .with_status_code(status_code)
68 .with_reason(reason.into())
69 .into()),
70 }
71 }
72}
73
74pub trait OptionExt {
76 type Item;
77
78 fn ok_or_status(self, status_code: StatusCode) -> anyhow::Result<Self::Item>;
91}
92
93impl<T> OptionExt for std::option::Option<T> {
94 type Item = T;
95
96 fn ok_or_status(self, status_code: StatusCode) -> anyhow::Result<T> {
97 match self {
98 Some(v) => Ok(v),
99 None => Err(HttpError::from_status_code(status_code).into()),
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use anyhow::anyhow;
107
108 use super::*;
109
110 #[test]
111 fn http_err_ext_result_map_status() {
112 let result: StdResult<(), _> = Err(anyhow!("error"));
113 let http_result = result.map_status(StatusCode::BAD_REQUEST);
114
115 let Err(e) = http_result else { unreachable!() };
116 let e: HttpError = e.into();
117 assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
118 assert_eq!(e.source.unwrap().to_string(), "error".to_owned());
119 }
120
121 #[test]
122 fn http_err_ext_result_map_http_error() {
123 let s = "nan".parse::<i32>().map_status(StatusCode::BAD_REQUEST);
124 let Err(e) = s else { unreachable!() };
125 let e: HttpError = e.into();
126 assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
127
128 let result = Err(anyhow!("error"));
129 let http_result: anyhow::Result<()> =
130 result.map_http_error(StatusCode::BAD_REQUEST, "invalid request");
131
132 let Err(e) = http_result else { unreachable!() };
133 let e: HttpError = e.into();
134 assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
135 assert_eq!(e.source.unwrap().to_string(), "error".to_owned());
136 assert_eq!(e.reason, Some("invalid request".into()));
137 }
138
139 #[test]
140 fn http_err_ext_option() {
141 let opt: Option<()> = None;
142 let http_result = opt.ok_or_status(StatusCode::BAD_REQUEST);
143
144 let Err(e) = http_result else { unreachable!() };
145 let e: HttpError = e.into();
146 assert_eq!(e.status_code, StatusCode::BAD_REQUEST);
147 assert!(e.source.is_none());
148 }
149}