watermelon_proto/
status_code.rs1use core::{
2 fmt::{self, Display, Formatter},
3 num::NonZeroU16,
4 str::FromStr,
5};
6
7use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
8
9use crate::util;
10
11#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
18pub struct StatusCode(NonZeroU16);
19
20impl StatusCode {
21 pub const IDLE_HEARTBEAT: StatusCode = Self::new_internal(100);
27 pub const OK: StatusCode = Self::new_internal(200);
29 pub const NOT_FOUND: StatusCode = Self::new_internal(404);
31 pub const TIMEOUT: StatusCode = Self::new_internal(408);
33 pub const NO_RESPONDERS: StatusCode = Self::new_internal(503);
35
36 pub fn from_ascii_bytes(buf: &[u8]) -> Result<Self, StatusCodeError> {
45 if buf.len() != 3 {
46 return Err(StatusCodeError);
47 }
48
49 util::parse_u16(buf)
50 .map_err(|_| StatusCodeError)?
51 .try_into()
52 .map(Self)
53 .map_err(|_| StatusCodeError)
54 }
55
56 const fn new_internal(val: u16) -> Self {
57 match NonZeroU16::new(val) {
58 Some(val) => Self(val),
59 None => unreachable!(),
60 }
61 }
62}
63
64impl FromStr for StatusCode {
65 type Err = StatusCodeError;
66
67 fn from_str(s: &str) -> Result<Self, Self::Err> {
68 Self::from_ascii_bytes(s.as_bytes())
69 }
70}
71
72impl Display for StatusCode {
73 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
74 Display::fmt(&self.0, f)
75 }
76}
77
78impl TryFrom<u16> for StatusCode {
79 type Error = StatusCodeError;
80
81 fn try_from(value: u16) -> Result<Self, Self::Error> {
82 if (100..1000).contains(&value) {
83 Ok(Self(NonZeroU16::new(value).unwrap()))
84 } else {
85 Err(StatusCodeError)
86 }
87 }
88}
89
90impl From<StatusCode> for u16 {
91 fn from(value: StatusCode) -> Self {
92 value.0.get()
93 }
94}
95
96impl Serialize for StatusCode {
97 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
98 u16::from(*self).serialize(serializer)
99 }
100}
101
102impl<'de> Deserialize<'de> for StatusCode {
103 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
104 let n = u16::deserialize(deserializer)?;
105 n.try_into().map_err(de::Error::custom)
106 }
107}
108
109#[derive(Debug, thiserror::Error)]
111#[non_exhaustive]
112#[error("invalid status code")]
113pub struct StatusCodeError;
114
115#[cfg(test)]
116mod tests {
117 use alloc::string::ToString;
118
119 use claims::assert_err;
120
121 use super::StatusCode;
122
123 #[test]
124 fn valid_status_codes() {
125 let status_codes = [100, 200, 404, 408, 409, 503];
126
127 for status_code in status_codes {
128 assert_eq!(
129 status_code,
130 u16::from(StatusCode::try_from(status_code).unwrap())
131 );
132
133 let s = status_code.to_string();
134 assert_eq!(
135 status_code,
136 u16::from(StatusCode::from_ascii_bytes(s.as_bytes()).unwrap())
137 );
138 }
139 }
140
141 #[test]
142 fn invalid_status_codes() {
143 let status_codes = [0, 5, 55, 9999];
144
145 for status_code in status_codes {
146 assert_err!(StatusCode::try_from(status_code));
147
148 let s = status_code.to_string();
149 assert_err!(StatusCode::from_ascii_bytes(s.as_bytes()));
150 }
151 }
152}