1use std::fmt;
4use std::str::FromStr;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct ParseGatewayRegStateError(pub String);
9
10impl fmt::Display for ParseGatewayRegStateError {
11 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12 write!(f, "unknown gateway reg state: {}", self.0)
13 }
14}
15
16impl std::error::Error for ParseGatewayRegStateError {}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
25#[non_exhaustive]
26#[allow(missing_docs)]
27pub enum GatewayRegState {
28 #[cfg_attr(feature = "serde", serde(alias = "Unreged"))]
29 Unreged,
30 #[cfg_attr(feature = "serde", serde(alias = "Trying"))]
31 Trying,
32 #[cfg_attr(feature = "serde", serde(alias = "Register"))]
33 Register,
34 #[cfg_attr(feature = "serde", serde(alias = "Reged"))]
35 Reged,
36 #[cfg_attr(feature = "serde", serde(alias = "Unregister"))]
37 Unregister,
38 #[cfg_attr(feature = "serde", serde(alias = "Failed"))]
39 Failed,
40 #[cfg_attr(feature = "serde", serde(alias = "FailWait"))]
41 FailWait,
42 #[cfg_attr(feature = "serde", serde(alias = "Expired"))]
43 Expired,
44 #[cfg_attr(feature = "serde", serde(alias = "Noreg"))]
45 Noreg,
46 #[cfg_attr(feature = "serde", serde(alias = "Down"))]
47 Down,
48 #[cfg_attr(feature = "serde", serde(alias = "Timeout"))]
49 Timeout,
50}
51
52impl GatewayRegState {
53 pub const fn as_str(&self) -> &'static str {
55 match self {
56 Self::Unreged => "UNREGED",
57 Self::Trying => "TRYING",
58 Self::Register => "REGISTER",
59 Self::Reged => "REGED",
60 Self::Unregister => "UNREGISTER",
61 Self::Failed => "FAILED",
62 Self::FailWait => "FAIL_WAIT",
63 Self::Expired => "EXPIRED",
64 Self::Noreg => "NOREG",
65 Self::Down => "DOWN",
66 Self::Timeout => "TIMEOUT",
67 }
68 }
69}
70
71impl fmt::Display for GatewayRegState {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 f.write_str(self.as_str())
74 }
75}
76
77impl FromStr for GatewayRegState {
78 type Err = ParseGatewayRegStateError;
79
80 fn from_str(s: &str) -> Result<Self, Self::Err> {
81 match s {
82 "UNREGED" => Ok(Self::Unreged),
83 "TRYING" => Ok(Self::Trying),
84 "REGISTER" => Ok(Self::Register),
85 "REGED" => Ok(Self::Reged),
86 "UNREGISTER" => Ok(Self::Unregister),
87 "FAILED" => Ok(Self::Failed),
88 "FAIL_WAIT" => Ok(Self::FailWait),
89 "EXPIRED" => Ok(Self::Expired),
90 "NOREG" => Ok(Self::Noreg),
91 "DOWN" => Ok(Self::Down),
92 "TIMEOUT" => Ok(Self::Timeout),
93 _ => Err(ParseGatewayRegStateError(s.to_string())),
94 }
95 }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct ParseGatewayPingStatusError(pub String);
101
102impl fmt::Display for ParseGatewayPingStatusError {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 write!(f, "unknown gateway ping status: {}", self.0)
105 }
106}
107
108impl std::error::Error for ParseGatewayPingStatusError {}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
114#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
115#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
116#[non_exhaustive]
117#[allow(missing_docs)]
118pub enum GatewayPingStatus {
119 #[cfg_attr(feature = "serde", serde(alias = "Down"))]
120 Down,
121 #[cfg_attr(feature = "serde", serde(alias = "Up"))]
122 Up,
123 #[cfg_attr(feature = "serde", serde(alias = "Invalid"))]
124 Invalid,
125}
126
127impl GatewayPingStatus {
128 pub const fn as_str(&self) -> &'static str {
130 match self {
131 Self::Down => "DOWN",
132 Self::Up => "UP",
133 Self::Invalid => "INVALID",
134 }
135 }
136}
137
138impl fmt::Display for GatewayPingStatus {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 f.write_str(self.as_str())
141 }
142}
143
144impl FromStr for GatewayPingStatus {
145 type Err = ParseGatewayPingStatusError;
146
147 fn from_str(s: &str) -> Result<Self, Self::Err> {
148 match s {
149 "DOWN" => Ok(Self::Down),
150 "UP" => Ok(Self::Up),
151 "INVALID" => Ok(Self::Invalid),
152 _ => Err(ParseGatewayPingStatusError(s.to_string())),
153 }
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 const ALL_REG_STATES: &[GatewayRegState] = &[
162 GatewayRegState::Unreged,
163 GatewayRegState::Trying,
164 GatewayRegState::Register,
165 GatewayRegState::Reged,
166 GatewayRegState::Unregister,
167 GatewayRegState::Failed,
168 GatewayRegState::FailWait,
169 GatewayRegState::Expired,
170 GatewayRegState::Noreg,
171 GatewayRegState::Down,
172 GatewayRegState::Timeout,
173 ];
174
175 #[test]
176 fn reg_state_round_trip() {
177 for &state in ALL_REG_STATES {
178 let wire = state.to_string();
179 let parsed: GatewayRegState = wire
180 .parse()
181 .unwrap();
182 assert_eq!(parsed, state, "round-trip failed for {wire}");
183 }
184 }
185
186 #[test]
187 fn reg_state_count() {
188 assert_eq!(ALL_REG_STATES.len(), 11);
189 }
190
191 #[test]
192 fn reg_state_rejects_unknown() {
193 assert!("BOGUS"
194 .parse::<GatewayRegState>()
195 .is_err());
196 assert!("reged"
197 .parse::<GatewayRegState>()
198 .is_err());
199 }
200
201 const ALL_PING_STATUSES: &[GatewayPingStatus] = &[
202 GatewayPingStatus::Down,
203 GatewayPingStatus::Up,
204 GatewayPingStatus::Invalid,
205 ];
206
207 #[test]
208 fn ping_status_round_trip() {
209 for &status in ALL_PING_STATUSES {
210 let wire = status.to_string();
211 let parsed: GatewayPingStatus = wire
212 .parse()
213 .unwrap();
214 assert_eq!(parsed, status, "round-trip failed for {wire}");
215 }
216 }
217
218 #[test]
219 fn ping_status_rejects_unknown() {
220 assert!("BOGUS"
221 .parse::<GatewayPingStatus>()
222 .is_err());
223 assert!("up"
224 .parse::<GatewayPingStatus>()
225 .is_err());
226 }
227
228 #[cfg(feature = "serde")]
229 mod serde_tests {
230 use super::*;
231
232 #[test]
233 fn reg_state_serde_uses_wire_format() {
234 for &state in ALL_REG_STATES {
235 let json = serde_json::to_string(&state).unwrap();
236 let expected = format!("\"{}\"", state.as_str());
237 assert_eq!(json, expected, "serde must serialize as wire format");
238 }
239 }
240
241 #[test]
242 fn reg_state_serde_round_trip() {
243 for &state in ALL_REG_STATES {
244 let json = serde_json::to_string(&state).unwrap();
245 let parsed: GatewayRegState = serde_json::from_str(&json).unwrap();
246 assert_eq!(parsed, state);
247 }
248 }
249
250 #[test]
251 fn reg_state_serde_accepts_pascal_case_alias() {
252 let cases = [
253 ("\"Noreg\"", GatewayRegState::Noreg),
254 ("\"Reged\"", GatewayRegState::Reged),
255 ("\"FailWait\"", GatewayRegState::FailWait),
256 ("\"Unreged\"", GatewayRegState::Unreged),
257 ];
258 for (json, expected) in cases {
259 let parsed: GatewayRegState = serde_json::from_str(json).unwrap();
260 assert_eq!(parsed, expected, "PascalCase alias failed for {json}");
261 }
262 }
263
264 #[test]
265 fn ping_status_serde_uses_wire_format() {
266 for &status in ALL_PING_STATUSES {
267 let json = serde_json::to_string(&status).unwrap();
268 let expected = format!("\"{}\"", status.as_str());
269 assert_eq!(json, expected, "serde must serialize as wire format");
270 }
271 }
272
273 #[test]
274 fn ping_status_serde_round_trip() {
275 for &status in ALL_PING_STATUSES {
276 let json = serde_json::to_string(&status).unwrap();
277 let parsed: GatewayPingStatus = serde_json::from_str(&json).unwrap();
278 assert_eq!(parsed, status);
279 }
280 }
281
282 #[test]
283 fn ping_status_serde_accepts_pascal_case_alias() {
284 let cases = [
285 ("\"Down\"", GatewayPingStatus::Down),
286 ("\"Up\"", GatewayPingStatus::Up),
287 ("\"Invalid\"", GatewayPingStatus::Invalid),
288 ];
289 for (json, expected) in cases {
290 let parsed: GatewayPingStatus = serde_json::from_str(json).unwrap();
291 assert_eq!(parsed, expected, "PascalCase alias failed for {json}");
292 }
293 }
294 }
295}