1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use std::fmt::{Display, Formatter, Result as FmtResult};

/// Status code of a response.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct StatusCode(u16);

impl StatusCode {
    /// Create a new status code from a raw status code.
    pub(crate) const fn new(code: u16) -> Self {
        // We don't need to do any checking that the status code is valid since
        // the value always comes from `hyper`'s status code implementation.
        Self(code)
    }

    /// Raw status code value.
    #[must_use = "status code must be used to be useful"]
    pub const fn raw(self) -> u16 {
        self.0
    }

    /// Whether the status code is informational.
    ///
    /// This is defined as being between `[100, 200)`.
    #[must_use = "whether a status code is informational must be used"]
    pub const fn is_informational(self) -> bool {
        self.in_range(100, 200)
    }

    /// Whether the status code is a success.
    ///
    /// This is defined as being between `[200, 300)`.
    #[must_use = "whether a status code is a success must be used"]
    pub const fn is_success(self) -> bool {
        self.in_range(200, 300)
    }

    /// Whether the status code is a redirection.
    ///
    /// This is defined as being between `[300, 400)`.
    #[must_use = "whether a status code is redirectional must be used"]
    pub const fn is_redirection(self) -> bool {
        self.in_range(300, 400)
    }

    /// Whether the status code is a client error.
    ///
    /// This is defined as being between `[400, 500)`.
    #[must_use = "whether a status code is a client error must be used"]
    pub const fn is_client_error(self) -> bool {
        self.in_range(400, 500)
    }

    /// Whether the status code is a server error.
    ///
    /// This is defined as being between `[500, 600)`.
    #[must_use = "whether a status code is a server error must be used"]
    pub const fn is_server_error(self) -> bool {
        self.in_range(500, 600)
    }

    /// Whether the status code is within a range.
    ///
    /// The range is defined as `[min, max)`.
    const fn in_range(self, min: u16, max: u16) -> bool {
        self.0 >= min && self.0 < max
    }
}

impl Display for StatusCode {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        self.0.fmt(f)
    }
}

#[cfg(test)]
mod tests {
    use super::StatusCode;
    use static_assertions::assert_impl_all;
    use std::{
        fmt::{Debug, Display},
        hash::Hash,
    };

    assert_impl_all!(
        StatusCode: Clone,
        Copy,
        Debug,
        Display,
        Eq,
        Hash,
        PartialEq,
        PartialOrd,
        Ord,
        Send,
        Sync
    );

    #[test]
    fn test_ranges() {
        assert!(StatusCode::new(100).is_informational());
        assert!(StatusCode::new(199).is_informational());
        assert!(StatusCode::new(200).is_success());
        assert!(StatusCode::new(299).is_success());
        assert!(StatusCode::new(300).is_redirection());
        assert!(StatusCode::new(399).is_redirection());
        assert!(StatusCode::new(400).is_client_error());
        assert!(StatusCode::new(499).is_client_error());
        assert!(StatusCode::new(500).is_server_error());
        assert!(StatusCode::new(599).is_server_error());
    }
}