armature_core/
status.rs

1// HTTP Status Codes
2
3/// HTTP status codes as defined in RFC 7231, RFC 6585, and additional standards
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum HttpStatus {
6    // 1xx Informational
7    Continue = 100,
8    SwitchingProtocols = 101,
9    Processing = 102,
10    EarlyHints = 103,
11
12    // 2xx Success
13    Ok = 200,
14    Created = 201,
15    Accepted = 202,
16    NonAuthoritativeInformation = 203,
17    NoContent = 204,
18    ResetContent = 205,
19    PartialContent = 206,
20    MultiStatus = 207,
21    AlreadyReported = 208,
22    ImUsed = 226,
23
24    // 3xx Redirection
25    MultipleChoices = 300,
26    MovedPermanently = 301,
27    Found = 302,
28    SeeOther = 303,
29    NotModified = 304,
30    UseProxy = 305,
31    TemporaryRedirect = 307,
32    PermanentRedirect = 308,
33
34    // 4xx Client Errors
35    BadRequest = 400,
36    Unauthorized = 401,
37    PaymentRequired = 402,
38    Forbidden = 403,
39    NotFound = 404,
40    MethodNotAllowed = 405,
41    NotAcceptable = 406,
42    ProxyAuthenticationRequired = 407,
43    RequestTimeout = 408,
44    Conflict = 409,
45    Gone = 410,
46    LengthRequired = 411,
47    PreconditionFailed = 412,
48    PayloadTooLarge = 413,
49    UriTooLong = 414,
50    UnsupportedMediaType = 415,
51    RangeNotSatisfiable = 416,
52    ExpectationFailed = 417,
53    ImATeapot = 418,
54    MisdirectedRequest = 421,
55    UnprocessableEntity = 422,
56    Locked = 423,
57    FailedDependency = 424,
58    TooEarly = 425,
59    UpgradeRequired = 426,
60    PreconditionRequired = 428,
61    TooManyRequests = 429,
62    RequestHeaderFieldsTooLarge = 431,
63    UnavailableForLegalReasons = 451,
64
65    // 5xx Server Errors
66    InternalServerError = 500,
67    NotImplemented = 501,
68    BadGateway = 502,
69    ServiceUnavailable = 503,
70    GatewayTimeout = 504,
71    HttpVersionNotSupported = 505,
72    VariantAlsoNegotiates = 506,
73    InsufficientStorage = 507,
74    LoopDetected = 508,
75    NotExtended = 510,
76    NetworkAuthenticationRequired = 511,
77}
78
79impl HttpStatus {
80    /// Get the numeric status code
81    pub fn code(&self) -> u16 {
82        *self as u16
83    }
84
85    /// Get the reason phrase for the status code
86    pub fn reason(&self) -> &'static str {
87        match self {
88            // 1xx
89            HttpStatus::Continue => "Continue",
90            HttpStatus::SwitchingProtocols => "Switching Protocols",
91            HttpStatus::Processing => "Processing",
92            HttpStatus::EarlyHints => "Early Hints",
93
94            // 2xx
95            HttpStatus::Ok => "OK",
96            HttpStatus::Created => "Created",
97            HttpStatus::Accepted => "Accepted",
98            HttpStatus::NonAuthoritativeInformation => "Non-Authoritative Information",
99            HttpStatus::NoContent => "No Content",
100            HttpStatus::ResetContent => "Reset Content",
101            HttpStatus::PartialContent => "Partial Content",
102            HttpStatus::MultiStatus => "Multi-Status",
103            HttpStatus::AlreadyReported => "Already Reported",
104            HttpStatus::ImUsed => "IM Used",
105
106            // 3xx
107            HttpStatus::MultipleChoices => "Multiple Choices",
108            HttpStatus::MovedPermanently => "Moved Permanently",
109            HttpStatus::Found => "Found",
110            HttpStatus::SeeOther => "See Other",
111            HttpStatus::NotModified => "Not Modified",
112            HttpStatus::UseProxy => "Use Proxy",
113            HttpStatus::TemporaryRedirect => "Temporary Redirect",
114            HttpStatus::PermanentRedirect => "Permanent Redirect",
115
116            // 4xx
117            HttpStatus::BadRequest => "Bad Request",
118            HttpStatus::Unauthorized => "Unauthorized",
119            HttpStatus::PaymentRequired => "Payment Required",
120            HttpStatus::Forbidden => "Forbidden",
121            HttpStatus::NotFound => "Not Found",
122            HttpStatus::MethodNotAllowed => "Method Not Allowed",
123            HttpStatus::NotAcceptable => "Not Acceptable",
124            HttpStatus::ProxyAuthenticationRequired => "Proxy Authentication Required",
125            HttpStatus::RequestTimeout => "Request Timeout",
126            HttpStatus::Conflict => "Conflict",
127            HttpStatus::Gone => "Gone",
128            HttpStatus::LengthRequired => "Length Required",
129            HttpStatus::PreconditionFailed => "Precondition Failed",
130            HttpStatus::PayloadTooLarge => "Payload Too Large",
131            HttpStatus::UriTooLong => "URI Too Long",
132            HttpStatus::UnsupportedMediaType => "Unsupported Media Type",
133            HttpStatus::RangeNotSatisfiable => "Range Not Satisfiable",
134            HttpStatus::ExpectationFailed => "Expectation Failed",
135            HttpStatus::ImATeapot => "I'm a teapot",
136            HttpStatus::MisdirectedRequest => "Misdirected Request",
137            HttpStatus::UnprocessableEntity => "Unprocessable Entity",
138            HttpStatus::Locked => "Locked",
139            HttpStatus::FailedDependency => "Failed Dependency",
140            HttpStatus::TooEarly => "Too Early",
141            HttpStatus::UpgradeRequired => "Upgrade Required",
142            HttpStatus::PreconditionRequired => "Precondition Required",
143            HttpStatus::TooManyRequests => "Too Many Requests",
144            HttpStatus::RequestHeaderFieldsTooLarge => "Request Header Fields Too Large",
145            HttpStatus::UnavailableForLegalReasons => "Unavailable For Legal Reasons",
146
147            // 5xx
148            HttpStatus::InternalServerError => "Internal Server Error",
149            HttpStatus::NotImplemented => "Not Implemented",
150            HttpStatus::BadGateway => "Bad Gateway",
151            HttpStatus::ServiceUnavailable => "Service Unavailable",
152            HttpStatus::GatewayTimeout => "Gateway Timeout",
153            HttpStatus::HttpVersionNotSupported => "HTTP Version Not Supported",
154            HttpStatus::VariantAlsoNegotiates => "Variant Also Negotiates",
155            HttpStatus::InsufficientStorage => "Insufficient Storage",
156            HttpStatus::LoopDetected => "Loop Detected",
157            HttpStatus::NotExtended => "Not Extended",
158            HttpStatus::NetworkAuthenticationRequired => "Network Authentication Required",
159        }
160    }
161
162    /// Check if status is informational (1xx)
163    pub fn is_informational(&self) -> bool {
164        (100..200).contains(&self.code())
165    }
166
167    /// Check if status is successful (2xx)
168    pub fn is_success(&self) -> bool {
169        (200..300).contains(&self.code())
170    }
171
172    /// Check if status is redirection (3xx)
173    pub fn is_redirection(&self) -> bool {
174        (300..400).contains(&self.code())
175    }
176
177    /// Check if status is client error (4xx)
178    pub fn is_client_error(&self) -> bool {
179        (400..500).contains(&self.code())
180    }
181
182    /// Check if status is server error (5xx)
183    pub fn is_server_error(&self) -> bool {
184        (500..600).contains(&self.code())
185    }
186
187    /// Check if status is an error (4xx or 5xx)
188    pub fn is_error(&self) -> bool {
189        self.is_client_error() || self.is_server_error()
190    }
191
192    /// Create status from u16 code
193    pub fn from_code(code: u16) -> Option<Self> {
194        match code {
195            // 1xx
196            100 => Some(HttpStatus::Continue),
197            101 => Some(HttpStatus::SwitchingProtocols),
198            102 => Some(HttpStatus::Processing),
199            103 => Some(HttpStatus::EarlyHints),
200
201            // 2xx
202            200 => Some(HttpStatus::Ok),
203            201 => Some(HttpStatus::Created),
204            202 => Some(HttpStatus::Accepted),
205            203 => Some(HttpStatus::NonAuthoritativeInformation),
206            204 => Some(HttpStatus::NoContent),
207            205 => Some(HttpStatus::ResetContent),
208            206 => Some(HttpStatus::PartialContent),
209            207 => Some(HttpStatus::MultiStatus),
210            208 => Some(HttpStatus::AlreadyReported),
211            226 => Some(HttpStatus::ImUsed),
212
213            // 3xx
214            300 => Some(HttpStatus::MultipleChoices),
215            301 => Some(HttpStatus::MovedPermanently),
216            302 => Some(HttpStatus::Found),
217            303 => Some(HttpStatus::SeeOther),
218            304 => Some(HttpStatus::NotModified),
219            305 => Some(HttpStatus::UseProxy),
220            307 => Some(HttpStatus::TemporaryRedirect),
221            308 => Some(HttpStatus::PermanentRedirect),
222
223            // 4xx
224            400 => Some(HttpStatus::BadRequest),
225            401 => Some(HttpStatus::Unauthorized),
226            402 => Some(HttpStatus::PaymentRequired),
227            403 => Some(HttpStatus::Forbidden),
228            404 => Some(HttpStatus::NotFound),
229            405 => Some(HttpStatus::MethodNotAllowed),
230            406 => Some(HttpStatus::NotAcceptable),
231            407 => Some(HttpStatus::ProxyAuthenticationRequired),
232            408 => Some(HttpStatus::RequestTimeout),
233            409 => Some(HttpStatus::Conflict),
234            410 => Some(HttpStatus::Gone),
235            411 => Some(HttpStatus::LengthRequired),
236            412 => Some(HttpStatus::PreconditionFailed),
237            413 => Some(HttpStatus::PayloadTooLarge),
238            414 => Some(HttpStatus::UriTooLong),
239            415 => Some(HttpStatus::UnsupportedMediaType),
240            416 => Some(HttpStatus::RangeNotSatisfiable),
241            417 => Some(HttpStatus::ExpectationFailed),
242            418 => Some(HttpStatus::ImATeapot),
243            421 => Some(HttpStatus::MisdirectedRequest),
244            422 => Some(HttpStatus::UnprocessableEntity),
245            423 => Some(HttpStatus::Locked),
246            424 => Some(HttpStatus::FailedDependency),
247            425 => Some(HttpStatus::TooEarly),
248            426 => Some(HttpStatus::UpgradeRequired),
249            428 => Some(HttpStatus::PreconditionRequired),
250            429 => Some(HttpStatus::TooManyRequests),
251            431 => Some(HttpStatus::RequestHeaderFieldsTooLarge),
252            451 => Some(HttpStatus::UnavailableForLegalReasons),
253
254            // 5xx
255            500 => Some(HttpStatus::InternalServerError),
256            501 => Some(HttpStatus::NotImplemented),
257            502 => Some(HttpStatus::BadGateway),
258            503 => Some(HttpStatus::ServiceUnavailable),
259            504 => Some(HttpStatus::GatewayTimeout),
260            505 => Some(HttpStatus::HttpVersionNotSupported),
261            506 => Some(HttpStatus::VariantAlsoNegotiates),
262            507 => Some(HttpStatus::InsufficientStorage),
263            508 => Some(HttpStatus::LoopDetected),
264            510 => Some(HttpStatus::NotExtended),
265            511 => Some(HttpStatus::NetworkAuthenticationRequired),
266
267            _ => None,
268        }
269    }
270}
271
272impl std::fmt::Display for HttpStatus {
273    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
274        write!(f, "{} {}", self.code(), self.reason())
275    }
276}
277
278impl From<HttpStatus> for u16 {
279    fn from(status: HttpStatus) -> Self {
280        status.code()
281    }
282}
283
284#[cfg(test)]
285mod tests {
286    use super::*;
287
288    #[test]
289    fn test_status_code() {
290        assert_eq!(HttpStatus::Ok.code(), 200);
291        assert_eq!(HttpStatus::NotFound.code(), 404);
292        assert_eq!(HttpStatus::InternalServerError.code(), 500);
293    }
294
295    #[test]
296    fn test_status_reason() {
297        assert_eq!(HttpStatus::Ok.reason(), "OK");
298        assert_eq!(HttpStatus::NotFound.reason(), "Not Found");
299        assert_eq!(
300            HttpStatus::InternalServerError.reason(),
301            "Internal Server Error"
302        );
303    }
304
305    #[test]
306    fn test_status_categories() {
307        assert!(HttpStatus::Ok.is_success());
308        assert!(HttpStatus::NotFound.is_client_error());
309        assert!(HttpStatus::InternalServerError.is_server_error());
310        assert!(HttpStatus::NotFound.is_error());
311        assert!(!HttpStatus::Ok.is_error());
312    }
313
314    #[test]
315    fn test_from_code() {
316        assert_eq!(HttpStatus::from_code(200), Some(HttpStatus::Ok));
317        assert_eq!(HttpStatus::from_code(404), Some(HttpStatus::NotFound));
318        assert_eq!(HttpStatus::from_code(999), None);
319    }
320
321    #[test]
322    fn test_display() {
323        assert_eq!(HttpStatus::Ok.to_string(), "200 OK");
324        assert_eq!(HttpStatus::NotFound.to_string(), "404 Not Found");
325    }
326}