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}