use crate::primitives::Str;
use core::fmt;
#[derive(Default, Debug, Clone, Copy)]
pub enum Bandwidth {
K,
#[default]
L,
M,
N,
O,
P,
X,
}
impl Bandwidth {
pub fn parse(caps: &Str) -> Option<Self> {
if caps.contains("K") {
return Some(Self::K);
}
if caps.contains("L") {
return Some(Self::L);
}
if caps.contains("M") {
return Some(Self::M);
}
if caps.contains("N") {
return Some(Self::N);
}
if caps.contains("O") {
return Some(Self::O);
}
if caps.contains("P") {
return Some(Self::P);
}
if caps.contains("X") {
return Some(Self::X);
}
None
}
}
impl From<usize> for Bandwidth {
fn from(value: usize) -> Self {
match value {
0..12_000 => Self::K,
12_000..48_000 => Self::L,
48_000..64_000 => Self::M,
64_000..128_000 => Self::N,
128_000..256_000 => Self::O,
256_000..2_000_000 => Self::P,
_ => Self::X,
}
}
}
impl From<Bandwidth> for Str {
fn from(value: Bandwidth) -> Self {
match value {
Bandwidth::K => Str::from("K"),
Bandwidth::L => Str::from("L"),
Bandwidth::M => Str::from("M"),
Bandwidth::N => Str::from("N"),
Bandwidth::O => Str::from("O"),
Bandwidth::P => Str::from("P"),
Bandwidth::X => Str::from("X"),
}
}
}
#[derive(Debug, Clone)]
pub struct Capabilities {
capabilities: Str,
floodfill: bool,
bandwidth: Option<Bandwidth>,
reachable: bool,
usable: bool,
}
impl fmt::Display for Capabilities {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.capabilities)
}
}
impl Capabilities {
pub fn parse(caps: &Str) -> Option<Self> {
let bandwidth = Bandwidth::parse(caps);
let floodfill = caps.contains("f");
let usable = !(caps.contains("E") || caps.contains("G"));
let reachable = !(caps.contains("U") || caps.contains("H"));
Some(Self {
capabilities: caps.clone(),
floodfill,
bandwidth,
usable,
reachable,
})
}
pub fn is_floodfill(&self) -> bool {
self.floodfill
}
pub fn is_fast(&self) -> bool {
self.bandwidth.is_some_and(|bandwidth| {
core::matches!(bandwidth, Bandwidth::O | Bandwidth::P | Bandwidth::X)
})
}
pub fn is_standard(&self) -> bool {
self.bandwidth.is_some_and(|bandwidth| {
core::matches!(
bandwidth,
Bandwidth::K | Bandwidth::L | Bandwidth::M | Bandwidth::N
)
})
}
pub fn is_reachable(&self) -> bool {
self.reachable
}
pub fn is_usable(&self) -> bool {
self.usable
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn standard_bandwidth() {
for cap in ["K", "L", "M", "N"] {
assert!(Capabilities::parse(&Str::from(cap)).unwrap().is_standard());
}
}
#[test]
fn high_bandwidth() {
for cap in ["O", "P", "X"] {
assert!(Capabilities::parse(&Str::from(cap)).unwrap().is_fast());
}
}
#[test]
fn unrecognized_bandwidth() {
let caps = Capabilities::parse(&Str::from("Z")).unwrap();
assert!(caps.bandwidth.is_none());
assert!(!caps.floodfill);
}
#[test]
fn floodfill() {
let caps = Capabilities::parse(&Str::from("Xf")).unwrap();
assert!(caps.is_floodfill());
assert!(caps.is_fast());
}
#[test]
fn usable() {
assert!(!Capabilities::parse(&Str::from("LG")).unwrap().is_usable());
assert!(!Capabilities::parse(&Str::from("LE")).unwrap().is_usable());
}
#[test]
fn reachable() {
assert!(!Capabilities::parse(&Str::from("HX")).unwrap().is_reachable());
assert!(!Capabilities::parse(&Str::from("UL")).unwrap().is_reachable());
}
}