use std::sync::atomic::{AtomicU8, Ordering};
use strum::{AsRefStr, Display, EnumString};
#[derive(Clone, Copy, Debug, Default, Display, Hash, PartialEq, Eq, AsRefStr, EnumString)]
#[repr(u8)]
#[strum(serialize_all = "UPPERCASE")]
pub enum ConnectionMode {
#[default]
Active = 0,
Reconnect = 1,
Disconnect = 2,
Closed = 3,
}
impl ConnectionMode {
#[inline]
#[must_use]
pub fn from_u8(value: u8) -> Self {
match value {
0 => Self::Active,
1 => Self::Reconnect,
2 => Self::Disconnect,
3 => Self::Closed,
_ => panic!("Invalid `ConnectionMode` value: {value}"),
}
}
#[inline]
#[must_use]
pub fn from_atomic(value: &AtomicU8) -> Self {
Self::from_u8(value.load(Ordering::SeqCst))
}
pub fn request_reconnect(value: &AtomicU8) -> bool {
value
.compare_exchange(
Self::Active.as_u8(),
Self::Reconnect.as_u8(),
Ordering::SeqCst,
Ordering::SeqCst,
)
.is_ok()
}
pub fn request_disconnect(value: &AtomicU8) -> bool {
value
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |mode| {
(!Self::from_u8(mode).is_closed()).then_some(Self::Disconnect.as_u8())
})
.is_ok()
}
#[inline]
#[must_use]
pub const fn as_u8(self) -> u8 {
self as u8
}
#[inline]
#[must_use]
pub const fn is_active(&self) -> bool {
matches!(self, Self::Active)
}
#[inline]
#[must_use]
pub const fn is_reconnect(&self) -> bool {
matches!(self, Self::Reconnect)
}
#[inline]
#[must_use]
pub const fn is_disconnect(&self) -> bool {
matches!(self, Self::Disconnect)
}
#[inline]
#[must_use]
pub const fn is_closed(&self) -> bool {
matches!(self, Self::Closed)
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
#[case(ConnectionMode::Active, true, ConnectionMode::Reconnect)]
#[case(ConnectionMode::Reconnect, false, ConnectionMode::Reconnect)]
#[case(ConnectionMode::Disconnect, false, ConnectionMode::Disconnect)]
#[case(ConnectionMode::Closed, false, ConnectionMode::Closed)]
fn request_reconnect_transitions(
#[case] start: ConnectionMode,
#[case] expected_result: bool,
#[case] expected_mode: ConnectionMode,
) {
let mode = AtomicU8::new(start.as_u8());
assert_eq!(ConnectionMode::request_reconnect(&mode), expected_result);
assert_eq!(ConnectionMode::from_atomic(&mode), expected_mode);
}
#[rstest]
#[case(ConnectionMode::Active, true, ConnectionMode::Disconnect)]
#[case(ConnectionMode::Reconnect, true, ConnectionMode::Disconnect)]
#[case(ConnectionMode::Disconnect, true, ConnectionMode::Disconnect)]
#[case(ConnectionMode::Closed, false, ConnectionMode::Closed)]
fn request_disconnect_transitions(
#[case] start: ConnectionMode,
#[case] expected_result: bool,
#[case] expected_mode: ConnectionMode,
) {
let mode = AtomicU8::new(start.as_u8());
assert_eq!(ConnectionMode::request_disconnect(&mode), expected_result);
assert_eq!(ConnectionMode::from_atomic(&mode), expected_mode);
}
}