spacegate_plugin/error.rs
1use std::fmt::Display;
2
3use hyper::{header::HeaderValue, Response, StatusCode};
4use spacegate_kernel::{SgBody, SgResponseExt};
5
6use crate::Plugin;
7
8/// # Usage
9/// 1. Create a plugin error with a status code
10/// ```rust ignore
11/// let x = result.map_err(PluginError::status::<MyPlugin, 404>);
12/// ```
13/// 2. Create a plugin error with a status 500
14/// ```rust ignore
15/// let x = result.map_err(PluginError::internal_error::<MyPlugin>);
16/// ```
17/// 3. Convert it into a response
18///
19/// Just use [`Response<SgBody>::from`].
20#[derive(Debug)]
21pub struct PluginError<E> {
22 plugin_code: &'static str,
23 source: E,
24 status: StatusCode,
25}
26
27const PLUGIN_ERROR_HEADER: &str = "X-Plugin-Error";
28
29impl<E> From<PluginError<E>> for Response<SgBody>
30where
31 E: Display,
32{
33 fn from(val: PluginError<E>) -> Self {
34 let mut resp = Response::with_code_message(val.status, val.to_string());
35 resp.headers_mut().insert(PLUGIN_ERROR_HEADER, HeaderValue::from_static(val.plugin_code));
36 resp
37 }
38}
39
40impl<E> PluginError<E> {
41 pub fn status<P: Plugin, const S: u16>(error: E) -> Self {
42 Self {
43 plugin_code: P::CODE,
44 source: error,
45 status: StatusCode::from_u16(S).expect("invalid status value"),
46 }
47 }
48 pub fn internal_error<P: Plugin>(e: E) -> Self {
49 Self {
50 plugin_code: P::CODE,
51 source: e,
52 status: StatusCode::INTERNAL_SERVER_ERROR,
53 }
54 }
55}
56
57impl<E> std::fmt::Display for PluginError<E>
58where
59 E: std::fmt::Display,
60{
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 write!(f, "[Sg.Plugin.{p}] {e}", p = self.plugin_code, e = self.source)
63 }
64}
65
66impl<E> std::error::Error for PluginError<E>
67where
68 E: std::error::Error + 'static,
69{
70 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
71 Some(&self.source)
72 }
73}
74
75pub mod code {
76 macro_rules! status_codes {
77 ($(
78 $(#[$docs:meta])*
79 ($code: expr, $key: ident, $reason: literal);
80 )*) => {
81 $(
82 $(#[$docs])*
83 pub const $key: u16 = $code;
84 )*
85
86 };
87 }
88 status_codes! {
89 /// 100 Continue
90 /// [[RFC7231, Section 6.2.1](https://tools.ietf.org/html/rfc7231#section-6.2.1)]
91 (100, CONTINUE, "Continue");
92 /// 101 Switching Protocols
93 /// [[RFC7231, Section 6.2.2](https://tools.ietf.org/html/rfc7231#section-6.2.2)]
94 (101, SWITCHING_PROTOCOLS, "Switching Protocols");
95 /// 102 Processing
96 /// [[RFC2518](https://tools.ietf.org/html/rfc2518)]
97 (102, PROCESSING, "Processing");
98
99 /// 200 OK
100 /// [[RFC7231, Section 6.3.1](https://tools.ietf.org/html/rfc7231#section-6.3.1)]
101 (200, OK, "OK");
102 /// 201 Created
103 /// [[RFC7231, Section 6.3.2](https://tools.ietf.org/html/rfc7231#section-6.3.2)]
104 (201, CREATED, "Created");
105 /// 202 Accepted
106 /// [[RFC7231, Section 6.3.3](https://tools.ietf.org/html/rfc7231#section-6.3.3)]
107 (202, ACCEPTED, "Accepted");
108 /// 203 Non-Authoritative Information
109 /// [[RFC7231, Section 6.3.4](https://tools.ietf.org/html/rfc7231#section-6.3.4)]
110 (203, NON_AUTHORITATIVE_INFORMATION, "Non Authoritative Information");
111 /// 204 No Content
112 /// [[RFC7231, Section 6.3.5](https://tools.ietf.org/html/rfc7231#section-6.3.5)]
113 (204, NO_CONTENT, "No Content");
114 /// 205 Reset Content
115 /// [[RFC7231, Section 6.3.6](https://tools.ietf.org/html/rfc7231#section-6.3.6)]
116 (205, RESET_CONTENT, "Reset Content");
117 /// 206 Partial Content
118 /// [[RFC7233, Section 4.1](https://tools.ietf.org/html/rfc7233#section-4.1)]
119 (206, PARTIAL_CONTENT, "Partial Content");
120 /// 207 Multi-Status
121 /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
122 (207, MULTI_STATUS, "Multi-Status");
123 /// 208 Already Reported
124 /// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
125 (208, ALREADY_REPORTED, "Already Reported");
126
127 /// 226 IM Used
128 /// [[RFC3229](https://tools.ietf.org/html/rfc3229)]
129 (226, IM_USED, "IM Used");
130
131 /// 300 Multiple Choices
132 /// [[RFC7231, Section 6.4.1](https://tools.ietf.org/html/rfc7231#section-6.4.1)]
133 (300, MULTIPLE_CHOICES, "Multiple Choices");
134 /// 301 Moved Permanently
135 /// [[RFC7231, Section 6.4.2](https://tools.ietf.org/html/rfc7231#section-6.4.2)]
136 (301, MOVED_PERMANENTLY, "Moved Permanently");
137 /// 302 Found
138 /// [[RFC7231, Section 6.4.3](https://tools.ietf.org/html/rfc7231#section-6.4.3)]
139 (302, FOUND, "Found");
140 /// 303 See Other
141 /// [[RFC7231, Section 6.4.4](https://tools.ietf.org/html/rfc7231#section-6.4.4)]
142 (303, SEE_OTHER, "See Other");
143 /// 304 Not Modified
144 /// [[RFC7232, Section 4.1](https://tools.ietf.org/html/rfc7232#section-4.1)]
145 (304, NOT_MODIFIED, "Not Modified");
146 /// 305 Use Proxy
147 /// [[RFC7231, Section 6.4.5](https://tools.ietf.org/html/rfc7231#section-6.4.5)]
148 (305, USE_PROXY, "Use Proxy");
149 /// 307 Temporary Redirect
150 /// [[RFC7231, Section 6.4.7](https://tools.ietf.org/html/rfc7231#section-6.4.7)]
151 (307, TEMPORARY_REDIRECT, "Temporary Redirect");
152 /// 308 Permanent Redirect
153 /// [[RFC7238](https://tools.ietf.org/html/rfc7238)]
154 (308, PERMANENT_REDIRECT, "Permanent Redirect");
155
156 /// 400 Bad Request
157 /// [[RFC7231, Section 6.5.1](https://tools.ietf.org/html/rfc7231#section-6.5.1)]
158 (400, BAD_REQUEST, "Bad Request");
159 /// 401 Unauthorized
160 /// [[RFC7235, Section 3.1](https://tools.ietf.org/html/rfc7235#section-3.1)]
161 (401, UNAUTHORIZED, "Unauthorized");
162 /// 402 Payment Required
163 /// [[RFC7231, Section 6.5.2](https://tools.ietf.org/html/rfc7231#section-6.5.2)]
164 (402, PAYMENT_REQUIRED, "Payment Required");
165 /// 403 Forbidden
166 /// [[RFC7231, Section 6.5.3](https://tools.ietf.org/html/rfc7231#section-6.5.3)]
167 (403, FORBIDDEN, "Forbidden");
168 /// 404 Not Found
169 /// [[RFC7231, Section 6.5.4](https://tools.ietf.org/html/rfc7231#section-6.5.4)]
170 (404, NOT_FOUND, "Not Found");
171 /// 405 Method Not Allowed
172 /// [[RFC7231, Section 6.5.5](https://tools.ietf.org/html/rfc7231#section-6.5.5)]
173 (405, METHOD_NOT_ALLOWED, "Method Not Allowed");
174 /// 406 Not Acceptable
175 /// [[RFC7231, Section 6.5.6](https://tools.ietf.org/html/rfc7231#section-6.5.6)]
176 (406, NOT_ACCEPTABLE, "Not Acceptable");
177 /// 407 Proxy Authentication Required
178 /// [[RFC7235, Section 3.2](https://tools.ietf.org/html/rfc7235#section-3.2)]
179 (407, PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required");
180 /// 408 Request Timeout
181 /// [[RFC7231, Section 6.5.7](https://tools.ietf.org/html/rfc7231#section-6.5.7)]
182 (408, REQUEST_TIMEOUT, "Request Timeout");
183 /// 409 Conflict
184 /// [[RFC7231, Section 6.5.8](https://tools.ietf.org/html/rfc7231#section-6.5.8)]
185 (409, CONFLICT, "Conflict");
186 /// 410 Gone
187 /// [[RFC7231, Section 6.5.9](https://tools.ietf.org/html/rfc7231#section-6.5.9)]
188 (410, GONE, "Gone");
189 /// 411 Length Required
190 /// [[RFC7231, Section 6.5.10](https://tools.ietf.org/html/rfc7231#section-6.5.10)]
191 (411, LENGTH_REQUIRED, "Length Required");
192 /// 412 Precondition Failed
193 /// [[RFC7232, Section 4.2](https://tools.ietf.org/html/rfc7232#section-4.2)]
194 (412, PRECONDITION_FAILED, "Precondition Failed");
195 /// 413 Payload Too Large
196 /// [[RFC7231, Section 6.5.11](https://tools.ietf.org/html/rfc7231#section-6.5.11)]
197 (413, PAYLOAD_TOO_LARGE, "Payload Too Large");
198 /// 414 URI Too Long
199 /// [[RFC7231, Section 6.5.12](https://tools.ietf.org/html/rfc7231#section-6.5.12)]
200 (414, URI_TOO_LONG, "URI Too Long");
201 /// 415 Unsupported Media Type
202 /// [[RFC7231, Section 6.5.13](https://tools.ietf.org/html/rfc7231#section-6.5.13)]
203 (415, UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
204 /// 416 Range Not Satisfiable
205 /// [[RFC7233, Section 4.4](https://tools.ietf.org/html/rfc7233#section-4.4)]
206 (416, RANGE_NOT_SATISFIABLE, "Range Not Satisfiable");
207 /// 417 Expectation Failed
208 /// [[RFC7231, Section 6.5.14](https://tools.ietf.org/html/rfc7231#section-6.5.14)]
209 (417, EXPECTATION_FAILED, "Expectation Failed");
210 /// 418 I'm a teapot
211 /// [curiously not registered by IANA but [RFC2324](https://tools.ietf.org/html/rfc2324)]
212 (418, IM_A_TEAPOT, "I'm a teapot");
213
214 /// 421 Misdirected Request
215 /// [RFC7540, Section 9.1.2](https://tools.ietf.org/html/rfc7540#section-9.1.2)
216 (421, MISDIRECTED_REQUEST, "Misdirected Request");
217 /// 422 Unprocessable Entity
218 /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
219 (422, UNPROCESSABLE_ENTITY, "Unprocessable Entity");
220 /// 423 Locked
221 /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
222 (423, LOCKED, "Locked");
223 /// 424 Failed Dependency
224 /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
225 (424, FAILED_DEPENDENCY, "Failed Dependency");
226
227 /// 426 Upgrade Required
228 /// [[RFC7231, Section 6.5.15](https://tools.ietf.org/html/rfc7231#section-6.5.15)]
229 (426, UPGRADE_REQUIRED, "Upgrade Required");
230
231 /// 428 Precondition Required
232 /// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
233 (428, PRECONDITION_REQUIRED, "Precondition Required");
234 /// 429 Too Many Requests
235 /// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
236 (429, TOO_MANY_REQUESTS, "Too Many Requests");
237
238 /// 431 Request Header Fields Too Large
239 /// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
240 (431, REQUEST_HEADER_FIELDS_TOO_LARGE, "Request Header Fields Too Large");
241
242 /// 451 Unavailable For Legal Reasons
243 /// [[RFC7725](https://tools.ietf.org/html/rfc7725)]
244 (451, UNAVAILABLE_FOR_LEGAL_REASONS, "Unavailable For Legal Reasons");
245
246 /// 500 Internal Server Error
247 /// [[RFC7231, Section 6.6.1](https://tools.ietf.org/html/rfc7231#section-6.6.1)]
248 (500, INTERNAL_SERVER_ERROR, "Internal Server Error");
249 /// 501 Not Implemented
250 /// [[RFC7231, Section 6.6.2](https://tools.ietf.org/html/rfc7231#section-6.6.2)]
251 (501, NOT_IMPLEMENTED, "Not Implemented");
252 /// 502 Bad Gateway
253 /// [[RFC7231, Section 6.6.3](https://tools.ietf.org/html/rfc7231#section-6.6.3)]
254 (502, BAD_GATEWAY, "Bad Gateway");
255 /// 503 Service Unavailable
256 /// [[RFC7231, Section 6.6.4](https://tools.ietf.org/html/rfc7231#section-6.6.4)]
257 (503, SERVICE_UNAVAILABLE, "Service Unavailable");
258 /// 504 Gateway Timeout
259 /// [[RFC7231, Section 6.6.5](https://tools.ietf.org/html/rfc7231#section-6.6.5)]
260 (504, GATEWAY_TIMEOUT, "Gateway Timeout");
261 /// 505 HTTP Version Not Supported
262 /// [[RFC7231, Section 6.6.6](https://tools.ietf.org/html/rfc7231#section-6.6.6)]
263 (505, HTTP_VERSION_NOT_SUPPORTED, "HTTP Version Not Supported");
264 /// 506 Variant Also Negotiates
265 /// [[RFC2295](https://tools.ietf.org/html/rfc2295)]
266 (506, VARIANT_ALSO_NEGOTIATES, "Variant Also Negotiates");
267 /// 507 Insufficient Storage
268 /// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
269 (507, INSUFFICIENT_STORAGE, "Insufficient Storage");
270 /// 508 Loop Detected
271 /// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
272 (508, LOOP_DETECTED, "Loop Detected");
273
274 /// 510 Not Extended
275 /// [[RFC2774](https://tools.ietf.org/html/rfc2774)]
276 (510, NOT_EXTENDED, "Not Extended");
277 /// 511 Network Authentication Required
278 /// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
279 (511, NETWORK_AUTHENTICATION_REQUIRED, "Network Authentication Required");
280 }
281}