watermelon_proto/
status_code.rs1use core::{
2 fmt::{self, Display, Formatter},
3 num::NonZero,
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(NonZero<u16>);
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 CONFLICT: StatusCode = Self::new_internal(409);
46 pub const NO_RESPONDERS: StatusCode = Self::new_internal(503);
48
49 pub fn from_ascii_bytes(buf: &[u8]) -> Result<Self, StatusCodeError> {
58 if buf.len() != 3 {
59 return Err(StatusCodeError);
60 }
61
62 util::parse_u16(buf)
63 .map_err(|_| StatusCodeError)?
64 .try_into()
65 .map(Self)
66 .map_err(|_| StatusCodeError)
67 }
68
69 const fn new_internal(val: u16) -> Self {
70 Self(NonZero::new(val).unwrap())
71 }
72}
73
74impl FromStr for StatusCode {
75 type Err = StatusCodeError;
76
77 fn from_str(s: &str) -> Result<Self, Self::Err> {
78 Self::from_ascii_bytes(s.as_bytes())
79 }
80}
81
82impl Display for StatusCode {
83 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
84 Display::fmt(&self.0, f)
85 }
86}
87
88impl TryFrom<u16> for StatusCode {
89 type Error = StatusCodeError;
90
91 fn try_from(value: u16) -> Result<Self, Self::Error> {
92 if (100..1000).contains(&value) {
93 Ok(Self(NonZero::new(value).unwrap()))
94 } else {
95 Err(StatusCodeError)
96 }
97 }
98}
99
100impl From<StatusCode> for u16 {
101 fn from(value: StatusCode) -> Self {
102 value.0.get()
103 }
104}
105
106impl Serialize for StatusCode {
107 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
108 u16::from(*self).serialize(serializer)
109 }
110}
111
112impl<'de> Deserialize<'de> for StatusCode {
113 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
114 let n = u16::deserialize(deserializer)?;
115 n.try_into().map_err(de::Error::custom)
116 }
117}
118
119#[derive(Debug, thiserror::Error)]
121#[non_exhaustive]
122#[error("invalid status code")]
123pub struct StatusCodeError;
124
125#[cfg(test)]
126mod tests {
127 use alloc::string::ToString;
128
129 use claims::assert_err;
130
131 use super::StatusCode;
132
133 #[test]
134 fn valid_status_codes() {
135 let status_codes = [100, 200, 404, 408, 409, 503];
136
137 for status_code in status_codes {
138 assert_eq!(
139 status_code,
140 u16::from(StatusCode::try_from(status_code).unwrap())
141 );
142
143 let s = status_code.to_string();
144 assert_eq!(
145 status_code,
146 u16::from(StatusCode::from_ascii_bytes(s.as_bytes()).unwrap())
147 );
148 }
149 }
150
151 #[test]
152 fn invalid_status_codes() {
153 let status_codes = [0, 5, 55, 9999];
154
155 for status_code in status_codes {
156 assert_err!(StatusCode::try_from(status_code));
157
158 let s = status_code.to_string();
159 assert_err!(StatusCode::from_ascii_bytes(s.as_bytes()));
160 }
161 }
162}