use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseGatewayRegStateError(pub String);
impl fmt::Display for ParseGatewayRegStateError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unknown gateway reg state: {}", self.0)
}
}
impl std::error::Error for ParseGatewayRegStateError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum GatewayRegState {
#[cfg_attr(feature = "serde", serde(alias = "Unreged"))]
Unreged,
#[cfg_attr(feature = "serde", serde(alias = "Trying"))]
Trying,
#[cfg_attr(feature = "serde", serde(alias = "Register"))]
Register,
#[cfg_attr(feature = "serde", serde(alias = "Reged"))]
Reged,
#[cfg_attr(feature = "serde", serde(alias = "Unregister"))]
Unregister,
#[cfg_attr(feature = "serde", serde(alias = "Failed"))]
Failed,
#[cfg_attr(feature = "serde", serde(alias = "FailWait"))]
FailWait,
#[cfg_attr(feature = "serde", serde(alias = "Expired"))]
Expired,
#[cfg_attr(feature = "serde", serde(alias = "Noreg"))]
Noreg,
#[cfg_attr(feature = "serde", serde(alias = "Down"))]
Down,
#[cfg_attr(feature = "serde", serde(alias = "Timeout"))]
Timeout,
}
impl GatewayRegState {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Unreged => "UNREGED",
Self::Trying => "TRYING",
Self::Register => "REGISTER",
Self::Reged => "REGED",
Self::Unregister => "UNREGISTER",
Self::Failed => "FAILED",
Self::FailWait => "FAIL_WAIT",
Self::Expired => "EXPIRED",
Self::Noreg => "NOREG",
Self::Down => "DOWN",
Self::Timeout => "TIMEOUT",
}
}
}
impl fmt::Display for GatewayRegState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for GatewayRegState {
type Err = ParseGatewayRegStateError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"UNREGED" => Ok(Self::Unreged),
"TRYING" => Ok(Self::Trying),
"REGISTER" => Ok(Self::Register),
"REGED" => Ok(Self::Reged),
"UNREGISTER" => Ok(Self::Unregister),
"FAILED" => Ok(Self::Failed),
"FAIL_WAIT" => Ok(Self::FailWait),
"EXPIRED" => Ok(Self::Expired),
"NOREG" => Ok(Self::Noreg),
"DOWN" => Ok(Self::Down),
"TIMEOUT" => Ok(Self::Timeout),
_ => Err(ParseGatewayRegStateError(s.to_string())),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseGatewayPingStatusError(pub String);
impl fmt::Display for ParseGatewayPingStatusError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unknown gateway ping status: {}", self.0)
}
}
impl std::error::Error for ParseGatewayPingStatusError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "SCREAMING_SNAKE_CASE"))]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum GatewayPingStatus {
#[cfg_attr(feature = "serde", serde(alias = "Down"))]
Down,
#[cfg_attr(feature = "serde", serde(alias = "Up"))]
Up,
#[cfg_attr(feature = "serde", serde(alias = "Invalid"))]
Invalid,
}
impl GatewayPingStatus {
pub const fn as_str(&self) -> &'static str {
match self {
Self::Down => "DOWN",
Self::Up => "UP",
Self::Invalid => "INVALID",
}
}
}
impl fmt::Display for GatewayPingStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for GatewayPingStatus {
type Err = ParseGatewayPingStatusError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"DOWN" => Ok(Self::Down),
"UP" => Ok(Self::Up),
"INVALID" => Ok(Self::Invalid),
_ => Err(ParseGatewayPingStatusError(s.to_string())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const ALL_REG_STATES: &[GatewayRegState] = &[
GatewayRegState::Unreged,
GatewayRegState::Trying,
GatewayRegState::Register,
GatewayRegState::Reged,
GatewayRegState::Unregister,
GatewayRegState::Failed,
GatewayRegState::FailWait,
GatewayRegState::Expired,
GatewayRegState::Noreg,
GatewayRegState::Down,
GatewayRegState::Timeout,
];
#[test]
fn reg_state_round_trip() {
for &state in ALL_REG_STATES {
let wire = state.to_string();
let parsed: GatewayRegState = wire
.parse()
.unwrap();
assert_eq!(parsed, state, "round-trip failed for {wire}");
}
}
#[test]
fn reg_state_count() {
assert_eq!(ALL_REG_STATES.len(), 11);
}
#[test]
fn reg_state_rejects_unknown() {
assert!("BOGUS"
.parse::<GatewayRegState>()
.is_err());
assert!("reged"
.parse::<GatewayRegState>()
.is_err());
}
const ALL_PING_STATUSES: &[GatewayPingStatus] = &[
GatewayPingStatus::Down,
GatewayPingStatus::Up,
GatewayPingStatus::Invalid,
];
#[test]
fn ping_status_round_trip() {
for &status in ALL_PING_STATUSES {
let wire = status.to_string();
let parsed: GatewayPingStatus = wire
.parse()
.unwrap();
assert_eq!(parsed, status, "round-trip failed for {wire}");
}
}
#[test]
fn ping_status_rejects_unknown() {
assert!("BOGUS"
.parse::<GatewayPingStatus>()
.is_err());
assert!("up"
.parse::<GatewayPingStatus>()
.is_err());
}
#[cfg(feature = "serde")]
mod serde_tests {
use super::*;
#[test]
fn reg_state_serde_uses_wire_format() {
for &state in ALL_REG_STATES {
let json = serde_json::to_string(&state).unwrap();
let expected = format!("\"{}\"", state.as_str());
assert_eq!(json, expected, "serde must serialize as wire format");
}
}
#[test]
fn reg_state_serde_round_trip() {
for &state in ALL_REG_STATES {
let json = serde_json::to_string(&state).unwrap();
let parsed: GatewayRegState = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, state);
}
}
#[test]
fn reg_state_serde_accepts_pascal_case_alias() {
let cases = [
("\"Noreg\"", GatewayRegState::Noreg),
("\"Reged\"", GatewayRegState::Reged),
("\"FailWait\"", GatewayRegState::FailWait),
("\"Unreged\"", GatewayRegState::Unreged),
];
for (json, expected) in cases {
let parsed: GatewayRegState = serde_json::from_str(json).unwrap();
assert_eq!(parsed, expected, "PascalCase alias failed for {json}");
}
}
#[test]
fn ping_status_serde_uses_wire_format() {
for &status in ALL_PING_STATUSES {
let json = serde_json::to_string(&status).unwrap();
let expected = format!("\"{}\"", status.as_str());
assert_eq!(json, expected, "serde must serialize as wire format");
}
}
#[test]
fn ping_status_serde_round_trip() {
for &status in ALL_PING_STATUSES {
let json = serde_json::to_string(&status).unwrap();
let parsed: GatewayPingStatus = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, status);
}
}
#[test]
fn ping_status_serde_accepts_pascal_case_alias() {
let cases = [
("\"Down\"", GatewayPingStatus::Down),
("\"Up\"", GatewayPingStatus::Up),
("\"Invalid\"", GatewayPingStatus::Invalid),
];
for (json, expected) in cases {
let parsed: GatewayPingStatus = serde_json::from_str(json).unwrap();
assert_eq!(parsed, expected, "PascalCase alias failed for {json}");
}
}
}
}