use std::fmt;
use anyhow::Result;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
bitflags::bitflags! {
#[derive(Default, PartialEq)]
pub struct LoginFlags: u8 {
const TRANSIT = 0x80;
const CONTINUE = 0x40;
const CSG_MASK = 0b0000_1100;
const NSG_MASK = 0b0000_0011;
}
}
impl TryFrom<u8> for LoginFlags {
type Error = anyhow::Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
LoginFlags::from_bits(value)
.ok_or_else(|| anyhow::anyhow!("invalid LoginFlags: {:#08b}", value))
}
}
impl fmt::Debug for LoginFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut parts = Vec::new();
if self.contains(LoginFlags::TRANSIT) {
parts.push("TRANSIT");
}
if self.contains(LoginFlags::CONTINUE) {
parts.push("CONTINUE");
}
match (self.bits() & LoginFlags::CSG_MASK.bits()) >> 2 {
0 => {},
1 => parts.push("CSG=Operational"),
3 => parts.push("CSG=FullFeature"),
_ => parts.push("CSG=Unknown"),
}
match self.bits() & LoginFlags::NSG_MASK.bits() {
0 => {},
1 => parts.push("NSG=Operational"),
3 => parts.push("NSG=FullFeature"),
_ => parts.push("NSG=Unknown"),
}
write!(f, "LoginFlags({})", parts.join("|"))
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq)]
#[repr(u8)]
pub enum Stage {
#[default]
Security = 0,
Operational = 1,
FullFeature = 3,
}
impl Stage {
pub fn from_bits(bits: u8) -> Option<Self> {
match bits & 0b11 {
0 => Some(Stage::Security),
1 => Some(Stage::Operational),
3 => Some(Stage::FullFeature),
_ => None,
}
}
}
#[repr(transparent)]
#[derive(
Copy, Clone, PartialEq, Eq, Default, FromBytes, IntoBytes, KnownLayout, Immutable,
)]
pub struct RawLoginFlags(u8);
impl RawLoginFlags {
#[inline]
pub const fn raw(self) -> u8 {
self.0
}
#[inline]
pub const fn from_raw(v: u8) -> Self {
Self(v)
}
#[inline]
pub fn flags(self) -> Result<LoginFlags> {
LoginFlags::try_from(self.0)
}
#[inline]
pub fn set_flags(&mut self, f: LoginFlags) {
self.0 = f.bits();
}
#[inline]
pub fn transit(self) -> bool {
(self.0 & LoginFlags::TRANSIT.bits()) != 0
}
#[inline]
pub fn set_transit(&mut self, on: bool) {
if on {
self.0 |= LoginFlags::TRANSIT.bits();
} else {
self.0 &= !LoginFlags::TRANSIT.bits();
}
}
#[inline]
pub fn cont(self) -> bool {
(self.0 & LoginFlags::CONTINUE.bits()) != 0
}
#[inline]
pub fn set_cont(&mut self, on: bool) {
if on {
self.0 |= LoginFlags::CONTINUE.bits();
} else {
self.0 &= !LoginFlags::CONTINUE.bits();
}
}
#[inline]
pub fn csg(self) -> Option<Stage> {
Stage::from_bits((self.0 & LoginFlags::CSG_MASK.bits()) >> 2)
}
#[inline]
pub fn set_csg(&mut self, s: Stage) {
self.0 = (self.0 & !LoginFlags::CSG_MASK.bits())
| (((s as u8) & LoginFlags::NSG_MASK.bits()) << 2);
}
#[inline]
pub fn nsg(self) -> Option<Stage> {
Stage::from_bits(self.0 & LoginFlags::NSG_MASK.bits())
}
#[inline]
pub fn set_nsg(&mut self, s: Stage) {
self.0 = (self.0 & !LoginFlags::NSG_MASK.bits())
| ((s as u8) & LoginFlags::NSG_MASK.bits());
}
}
impl TryFrom<RawLoginFlags> for LoginFlags {
type Error = anyhow::Error;
#[inline]
fn try_from(r: RawLoginFlags) -> Result<Self> {
LoginFlags::try_from(r.raw())
}
}
impl From<LoginFlags> for RawLoginFlags {
#[inline]
fn from(f: LoginFlags) -> Self {
Self(f.bits())
}
}
impl fmt::Debug for RawLoginFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RawLoginFlags({:?})", LoginFlags::try_from(self.0))
}
}