twilight_http/response/
status_code.rs

1use std::fmt::{Display, Formatter, Result as FmtResult};
2
3/// Status code of a response.
4///
5/// Constants are defined for the response codes that Discord defines in their [response code table].
6///
7/// # Comparing the status code
8///
9/// Status codes can easily be compared with status code integers due to
10/// implementing `PartialEq<u16>`. This is equivalent to checking against the
11/// value returned by [`StatusCode::get`].
12///
13/// [response code table]: https://discord.com/developers/docs/topics/opcodes-and-status-codes#http-http-response-codes
14#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
15pub struct StatusCode(u16);
16
17impl StatusCode {
18    /// Create a new status code from a raw status code.
19    pub(crate) const fn new(code: u16) -> Self {
20        // We don't need to do any checking that the status code is valid since
21        // the value always comes from `hyper`'s status code implementation.
22        Self(code)
23    }
24
25    /// Raw status code value.
26    #[must_use = "status code must be used to be useful"]
27    pub const fn get(self) -> u16 {
28        self.0
29    }
30
31    /// Whether the status code is informational.
32    ///
33    /// This is defined as being between `[100, 200)`.
34    #[must_use = "whether a status code is informational must be used"]
35    pub const fn is_informational(self) -> bool {
36        self.in_range(100, 200)
37    }
38
39    /// Whether the status code is a success.
40    ///
41    /// This is defined as being between `[200, 300)`.
42    #[must_use = "whether a status code is a success must be used"]
43    pub const fn is_success(self) -> bool {
44        self.in_range(200, 300)
45    }
46
47    /// Whether the status code is a redirection.
48    ///
49    /// This is defined as being between `[300, 400)`.
50    #[must_use = "whether a status code is redirectional must be used"]
51    pub const fn is_redirection(self) -> bool {
52        self.in_range(300, 400)
53    }
54
55    /// Whether the status code is a client error.
56    ///
57    /// This is defined as being between `[400, 500)`.
58    #[must_use = "whether a status code is a client error must be used"]
59    pub const fn is_client_error(self) -> bool {
60        self.in_range(400, 500)
61    }
62
63    /// Whether the status code is a server error.
64    ///
65    /// This is defined as being between `[500, 600)`.
66    #[must_use = "whether a status code is a server error must be used"]
67    pub const fn is_server_error(self) -> bool {
68        self.in_range(500, 600)
69    }
70
71    /// Whether the status code is within a range.
72    ///
73    /// The range is defined as `[min, max)`.
74    const fn in_range(self, min: u16, max: u16) -> bool {
75        self.0 >= min && self.0 < max
76    }
77
78    /// 200 (OK) The request completed successfully.
79    pub const OK: StatusCode = StatusCode::new(200);
80
81    /// 201 (CREATED) The entity was created successfully.
82    pub const CREATED: StatusCode = StatusCode::new(201);
83
84    /// 204 (NO CONTENT) The request completed successfully but returned no content.
85    pub const NO_CONTENT: StatusCode = StatusCode::new(204);
86
87    /// 304 (NOT MODIFIED) The entity was not modified (no action was taken).
88    pub const NOT_MODIFIED: StatusCode = StatusCode::new(304);
89
90    /// 400 (BAD REQUEST) The request was improperly formatted, or the server couldn't understand it.
91    pub const BAD_REQUEST: StatusCode = StatusCode::new(400);
92
93    /// 401 (UNAUTHORIZED) The Authorization header was missing or invalid.
94    pub const UNAUTHORIZED: StatusCode = StatusCode::new(401);
95
96    /// 403 (FORBIDDEN) The Authorization token you passed did not have permission to the resource.
97    pub const FORBIDDEN: StatusCode = StatusCode::new(403);
98
99    /// 404 (NOT FOUND) The resource at the location specified doesn't exist.
100    pub const NOT_FOUND: StatusCode = StatusCode::new(404);
101
102    /// 405 (METHOD NOT ALLOWED) The HTTP method used is not valid for the location specified.
103    pub const METHOD_NOT_ALLOWED: StatusCode = StatusCode::new(405);
104
105    /// 429 (TOO MANY REQUESTS) You are being rate limited, see Rate Limits.
106    pub const TOO_MANY_REQUESTS: StatusCode = StatusCode::new(429);
107
108    /// 502 (GATEWAY UNAVAILABLE) There was not a gateway available to process your request. Wait a bit and retry.
109    pub const GATEWAY_UNAVAILABLE: StatusCode = StatusCode::new(502);
110}
111
112impl Display for StatusCode {
113    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
114        self.0.fmt(f)
115    }
116}
117
118impl PartialEq<u16> for StatusCode {
119    fn eq(&self, other: &u16) -> bool {
120        self.get() == *other
121    }
122}
123
124impl PartialEq<StatusCode> for u16 {
125    fn eq(&self, other: &StatusCode) -> bool {
126        *self == other.get()
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::StatusCode;
133    use static_assertions::assert_impl_all;
134    use std::{
135        fmt::{Debug, Display},
136        hash::Hash,
137    };
138
139    assert_impl_all!(
140        StatusCode: Clone,
141        Copy,
142        Debug,
143        Display,
144        Eq,
145        Hash,
146        PartialEq,
147        PartialOrd,
148        Ord,
149        Send,
150        Sync
151    );
152
153    #[test]
154    fn eq_with_integer() {
155        assert_eq!(200_u16, StatusCode::new(200));
156        assert_eq!(StatusCode::new(404), 404_u16);
157    }
158
159    /// Test that [`StatusCode::get`] returns the raw value of the status code.
160    ///
161    /// Notably what we want to test here is that it's not repeatedly returning
162    /// the same value (as if it were hardcoded), and that it's instead
163    /// returning the provided value.
164    #[test]
165    fn get() {
166        assert_eq!(200, StatusCode::new(200).get());
167        assert_eq!(403, StatusCode::new(403).get());
168        assert_eq!(404, StatusCode::new(404).get());
169    }
170
171    #[test]
172    fn ranges() {
173        assert!(StatusCode::new(100).is_informational());
174        assert!(StatusCode::new(199).is_informational());
175        assert!(StatusCode::new(200).is_success());
176        assert!(StatusCode::new(299).is_success());
177        assert!(StatusCode::new(300).is_redirection());
178        assert!(StatusCode::new(399).is_redirection());
179        assert!(StatusCode::new(400).is_client_error());
180        assert!(StatusCode::new(499).is_client_error());
181        assert!(StatusCode::new(500).is_server_error());
182        assert!(StatusCode::new(599).is_server_error());
183    }
184}