use thiserror::Error;
use core::fmt;
use core::str::FromStr;
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Vulnerability: u8 {
const NS = 1;
const EW = 2;
}
}
impl Vulnerability {
pub const ALL: Self = Self::all();
pub const NONE: Self = Self::empty();
#[must_use]
#[inline]
pub const fn to_sys(self) -> i32 {
const ALL: u8 = Vulnerability::all().bits();
const NS: u8 = Vulnerability::NS.bits();
const EW: u8 = Vulnerability::EW.bits();
match self.bits() {
0 => 0,
ALL => 1,
NS => 2,
EW => 3,
_ => unreachable!(),
}
}
#[must_use]
#[inline]
pub const fn rotate(self, condition: bool) -> Self {
Self::from_bits_truncate((self.bits() * 0x55) >> (condition as u8))
}
}
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
#[error("Invalid vulnerability: expected one of none, ns, ew, both")]
pub struct ParseVulnerabilityError;
impl FromStr for Vulnerability {
type Err = ParseVulnerabilityError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"none" => Ok(Self::NONE),
"ns" => Ok(Self::NS),
"ew" => Ok(Self::EW),
"both" | "all" => Ok(Self::ALL),
_ => Err(ParseVulnerabilityError),
}
}
}
impl fmt::Display for Vulnerability {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self {
Self::NONE => "none",
Self::NS => "ns",
Self::EW => "ew",
Self::ALL => "both",
_ => unreachable!(),
})
}
}
const _: () = {
const ALL: Vulnerability = Vulnerability::all();
const NONE: Vulnerability = Vulnerability::empty();
assert!(matches!(ALL.rotate(true), ALL));
assert!(matches!(NONE.rotate(true), NONE));
assert!(matches!(Vulnerability::NS.rotate(true), Vulnerability::EW));
assert!(matches!(Vulnerability::EW.rotate(true), Vulnerability::NS));
assert!(matches!(ALL.rotate(false), ALL));
assert!(matches!(NONE.rotate(false), NONE));
assert!(matches!(Vulnerability::NS.rotate(false), Vulnerability::NS));
assert!(matches!(Vulnerability::EW.rotate(false), Vulnerability::EW));
};