1use std::borrow::Cow;
2use std::{error, fmt};
3
4use http::StatusCode;
5
6use crate::HttpError;
7
8#[derive(Debug)]
9pub struct StatusError {
10 status: StatusCode,
11 context: Cow<'static, str>,
12 #[cfg(feature = "tracing")]
13 span: tracing::Span,
14}
15
16impl StatusError {
17 pub fn new(status: StatusCode, context: impl Into<Cow<'static, str>>) -> Self {
18 Self {
19 status,
20 context: context.into(),
21 #[cfg(feature = "tracing")]
22 span: tracing::Span::current(),
23 }
24 }
25}
26
27macro_rules! trait_fn {
28 ($name:ident) => {
29 fn $name(self, context: impl Into<Cow<'static, str>>) -> Result<T, StatusError>;
30 };
31}
32macro_rules! impl_fn {
33 ($name:ident, $status:ident) => {
34 fn $name(self, context: impl Into<Cow<'static, str>>) -> Result<T, StatusError> {
35 self.ok_or_else(|| StatusError::new(StatusCode::$status, context))
36 }
37 };
38}
39
40pub trait OptionExt<T> {
41 trait_fn!(ok_or_bad_request);
43 trait_fn!(ok_or_unauthorized);
45 trait_fn!(ok_or_payment_required);
47 trait_fn!(ok_or_forbidden);
49 trait_fn!(ok_or_not_found);
51 trait_fn!(ok_or_method_not_allowed);
53 trait_fn!(ok_or_not_acceptable);
55 trait_fn!(ok_or_proxy_authentication_required);
57 trait_fn!(ok_or_request_timeout);
59 trait_fn!(ok_or_conflict);
61 trait_fn!(ok_or_gone);
63 trait_fn!(ok_or_length_required);
65 trait_fn!(ok_or_precondition_failed);
67 trait_fn!(ok_or_payload_too_large);
69 trait_fn!(ok_or_uri_too_long);
71 trait_fn!(ok_or_unsupported_media_type);
73 trait_fn!(ok_or_range_not_satisfiable);
75 trait_fn!(ok_or_expectation_failed);
77 trait_fn!(ok_or_im_a_teapot);
79
80 trait_fn!(ok_or_misdirected_request);
82 trait_fn!(ok_or_unprocessable_entity);
84 trait_fn!(ok_or_locked);
86 trait_fn!(ok_or_failed_dependency);
88
89 trait_fn!(ok_or_upgrade_required);
91
92 trait_fn!(ok_or_precondition_required);
94 trait_fn!(ok_or_too_many_requests);
96
97 trait_fn!(ok_or_request_header_fields_too_large);
99
100 trait_fn!(ok_or_unavailable_for_legal_reasons);
102
103 trait_fn!(ok_or_internal_server_error);
105 trait_fn!(ok_or_not_implemented);
107 trait_fn!(ok_or_bad_gateway);
109 trait_fn!(ok_or_service_unavailable);
111 trait_fn!(ok_or_gateway_timeout);
113 trait_fn!(ok_or_http_version_not_supported);
115 trait_fn!(ok_or_variant_also_negotiates);
117 trait_fn!(ok_or_insufficient_storage);
119 trait_fn!(ok_or_loop_detected);
121
122 trait_fn!(ok_or_not_extended);
124 trait_fn!(ok_or_network_authentication_required);
126}
127impl<T> OptionExt<T> for Option<T> {
128 impl_fn!(ok_or_bad_request, BAD_REQUEST);
129 impl_fn!(ok_or_unauthorized, UNAUTHORIZED);
130 impl_fn!(ok_or_payment_required, PAYMENT_REQUIRED);
131 impl_fn!(ok_or_forbidden, FORBIDDEN);
132 impl_fn!(ok_or_not_found, NOT_FOUND);
133 impl_fn!(ok_or_method_not_allowed, METHOD_NOT_ALLOWED);
134 impl_fn!(ok_or_not_acceptable, NOT_ACCEPTABLE);
135 impl_fn!(
136 ok_or_proxy_authentication_required,
137 PROXY_AUTHENTICATION_REQUIRED
138 );
139 impl_fn!(ok_or_request_timeout, REQUEST_TIMEOUT);
140 impl_fn!(ok_or_conflict, CONFLICT);
141 impl_fn!(ok_or_gone, GONE);
142 impl_fn!(ok_or_length_required, LENGTH_REQUIRED);
143 impl_fn!(ok_or_precondition_failed, PRECONDITION_FAILED);
144 impl_fn!(ok_or_payload_too_large, PAYLOAD_TOO_LARGE);
145 impl_fn!(ok_or_uri_too_long, URI_TOO_LONG);
146 impl_fn!(ok_or_unsupported_media_type, UNSUPPORTED_MEDIA_TYPE);
147 impl_fn!(ok_or_range_not_satisfiable, RANGE_NOT_SATISFIABLE);
148 impl_fn!(ok_or_expectation_failed, EXPECTATION_FAILED);
149 impl_fn!(ok_or_im_a_teapot, IM_A_TEAPOT);
150
151 impl_fn!(ok_or_misdirected_request, MISDIRECTED_REQUEST);
152 impl_fn!(ok_or_unprocessable_entity, UNPROCESSABLE_ENTITY);
153 impl_fn!(ok_or_locked, LOCKED);
154 impl_fn!(ok_or_failed_dependency, FAILED_DEPENDENCY);
155
156 impl_fn!(ok_or_upgrade_required, UPGRADE_REQUIRED);
157
158 impl_fn!(ok_or_precondition_required, PRECONDITION_REQUIRED);
159 impl_fn!(ok_or_too_many_requests, TOO_MANY_REQUESTS);
160
161 impl_fn!(
162 ok_or_request_header_fields_too_large,
163 REQUEST_HEADER_FIELDS_TOO_LARGE
164 );
165
166 impl_fn!(
167 ok_or_unavailable_for_legal_reasons,
168 UNAVAILABLE_FOR_LEGAL_REASONS
169 );
170
171 impl_fn!(ok_or_internal_server_error, INTERNAL_SERVER_ERROR);
172 impl_fn!(ok_or_not_implemented, NOT_IMPLEMENTED);
173 impl_fn!(ok_or_bad_gateway, BAD_GATEWAY);
174 impl_fn!(ok_or_service_unavailable, SERVICE_UNAVAILABLE);
175 impl_fn!(ok_or_gateway_timeout, GATEWAY_TIMEOUT);
176 impl_fn!(ok_or_http_version_not_supported, HTTP_VERSION_NOT_SUPPORTED);
177 impl_fn!(ok_or_variant_also_negotiates, VARIANT_ALSO_NEGOTIATES);
178 impl_fn!(ok_or_insufficient_storage, INSUFFICIENT_STORAGE);
179 impl_fn!(ok_or_loop_detected, LOOP_DETECTED);
180
181 impl_fn!(ok_or_not_extended, NOT_EXTENDED);
182 impl_fn!(
183 ok_or_network_authentication_required,
184 NETWORK_AUTHENTICATION_REQUIRED
185 );
186}
187
188impl error::Error for StatusError {
189 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
190 None
191 }
192}
193
194impl fmt::Display for StatusError {
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 f.write_str(&self.context)
197 }
198}
199
200impl HttpError for StatusError {
201 fn status_code(&self) -> StatusCode {
202 self.status
203 }
204
205 #[cfg(feature = "tracing")]
206 fn span(&self) -> Option<&tracing::Span> {
207 Some(&self.span)
208 }
209}