1use std::convert::TryFrom;
19use std::error::Error;
20use std::fmt;
21use std::num::NonZeroU16;
22use std::str::FromStr;
23
24use serde::{Deserialize, Serialize};
25
26pub struct InvalidStatusCode {}
31
32impl fmt::Debug for InvalidStatusCode {
33 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34 f.debug_struct("InvalidStatusCode").finish()
35 }
36}
37
38impl fmt::Display for InvalidStatusCode {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 f.write_str("invalid status code")
41 }
42}
43
44impl Error for InvalidStatusCode {}
45
46impl InvalidStatusCode {
47 fn new() -> InvalidStatusCode {
48 InvalidStatusCode {}
49 }
50}
51
52#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
69pub struct StatusCode(NonZeroU16);
70
71impl StatusCode {
72 #[inline]
92 pub fn from_u16(src: u16) -> Result<StatusCode, InvalidStatusCode> {
93 if !(100..1000).contains(&src) {
94 return Err(InvalidStatusCode::new());
95 }
96
97 NonZeroU16::new(src)
98 .map(StatusCode)
99 .ok_or_else(InvalidStatusCode::new)
100 }
101
102 pub fn from_bytes(src: &[u8]) -> Result<StatusCode, InvalidStatusCode> {
104 if src.len() != 3 {
105 return Err(InvalidStatusCode::new());
106 }
107
108 let a = src[0].wrapping_sub(b'0') as u16;
109 let b = src[1].wrapping_sub(b'0') as u16;
110 let c = src[2].wrapping_sub(b'0') as u16;
111
112 if a == 0 || a > 9 || b > 9 || c > 9 {
113 return Err(InvalidStatusCode::new());
114 }
115
116 let status = (a * 100) + (b * 10) + c;
117 NonZeroU16::new(status)
118 .map(StatusCode)
119 .ok_or_else(InvalidStatusCode::new)
120 }
121
122 #[inline]
131 pub fn as_u16(&self) -> u16 {
132 (*self).into()
133 }
134
135 #[inline]
137 pub fn is_informational(&self) -> bool {
138 (100..200).contains(&self.0.get())
139 }
140
141 #[inline]
143 pub fn is_success(&self) -> bool {
144 (200..300).contains(&self.0.get())
145 }
146
147 #[inline]
149 pub fn is_redirection(&self) -> bool {
150 (300..400).contains(&self.0.get())
151 }
152
153 #[inline]
155 pub fn is_client_error(&self) -> bool {
156 (400..500).contains(&self.0.get())
157 }
158
159 #[inline]
161 pub fn is_server_error(&self) -> bool {
162 (500..600).contains(&self.0.get())
163 }
164}
165
166impl fmt::Debug for StatusCode {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 fmt::Debug::fmt(&self.0, f)
169 }
170}
171
172impl fmt::Display for StatusCode {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 write!(f, "{}", u16::from(*self))
184 }
185}
186
187impl Default for StatusCode {
188 #[inline]
189 fn default() -> StatusCode {
190 StatusCode::OK
191 }
192}
193
194impl PartialEq<u16> for StatusCode {
195 #[inline]
196 fn eq(&self, other: &u16) -> bool {
197 self.as_u16() == *other
198 }
199}
200
201impl PartialEq<StatusCode> for u16 {
202 #[inline]
203 fn eq(&self, other: &StatusCode) -> bool {
204 *self == other.as_u16()
205 }
206}
207
208impl From<StatusCode> for u16 {
209 #[inline]
210 fn from(status: StatusCode) -> u16 {
211 status.0.get()
212 }
213}
214
215impl FromStr for StatusCode {
216 type Err = InvalidStatusCode;
217
218 fn from_str(s: &str) -> Result<StatusCode, InvalidStatusCode> {
219 StatusCode::from_bytes(s.as_ref())
220 }
221}
222
223impl<'a> From<&'a StatusCode> for StatusCode {
224 #[inline]
225 fn from(t: &'a StatusCode) -> Self {
226 *t
227 }
228}
229
230impl<'a> TryFrom<&'a [u8]> for StatusCode {
231 type Error = InvalidStatusCode;
232
233 #[inline]
234 fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
235 StatusCode::from_bytes(t)
236 }
237}
238
239impl<'a> TryFrom<&'a str> for StatusCode {
240 type Error = InvalidStatusCode;
241
242 #[inline]
243 fn try_from(t: &'a str) -> Result<Self, Self::Error> {
244 t.parse()
245 }
246}
247
248impl TryFrom<u16> for StatusCode {
249 type Error = InvalidStatusCode;
250
251 #[inline]
252 fn try_from(t: u16) -> Result<Self, Self::Error> {
253 StatusCode::from_u16(t)
254 }
255}
256
257impl StatusCode {
258 pub const IDLE_HEARTBEAT: StatusCode = StatusCode(new_nonzero_u16(100));
259 pub const OK: StatusCode = StatusCode(new_nonzero_u16(200));
260 pub const NOT_FOUND: StatusCode = StatusCode(new_nonzero_u16(404));
261 pub const TIMEOUT: StatusCode = StatusCode(new_nonzero_u16(408));
262 pub const NO_RESPONDERS: StatusCode = StatusCode(new_nonzero_u16(503));
263 pub const REQUEST_TERMINATED: StatusCode = StatusCode(new_nonzero_u16(409));
264}
265
266const fn new_nonzero_u16(n: u16) -> NonZeroU16 {
269 match NonZeroU16::new(n) {
270 Some(d) => d,
271 None => {
272 panic!("Invalid non-zero u16");
273 }
274 }
275}