kawaiifi 0.2.1

Wi-Fi scanning library for Linux, macOS, and Windows.
Documentation
use std::fmt::Display;

use deku::{DekuContainerWrite, DekuRead, DekuWrite};
use serde::{Deserialize, Serialize};

use crate::ies::{BitRange, Field, IeId};

#[derive(Debug, Clone, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(ctx = "len: usize")]
pub struct Interworking {
    pub access_network_options: AccessNetworkOptions,
    #[deku(cond = "len == 3 || len == 9")]
    pub venue_info: Option<VenueInfo>,
    #[deku(cond = "len == 7 || len == 9")]
    pub hessid: Option<[u8; 6]>,
}

impl Interworking {
    pub const ID: u8 = 107;
    pub const ID_EXT: Option<u8> = None;
    pub(crate) const IE_ID: IeId = IeId::new(Self::ID, Self::ID_EXT);
    pub const NAME: &'static str = "Interworking";

    pub fn summary(&self) -> String {
        format!(
            "Access Network Type: {}",
            self.access_network_options.access_network_type
        )
    }

    pub fn fields(&self) -> Vec<Field> {
        let mut fields = vec![self.access_network_options.to_field()];

        if let Some(venue_info) = self.venue_info {
            fields.push(venue_info.to_field());
        }

        if let Some(hessid) = self.hessid {
            fields.push(
                Field::builder()
                    .title("HESSID")
                    .value(
                        hessid
                            .iter()
                            .map(|byte| format!("{:02X}", byte))
                            .collect::<Vec<String>>()
                            .join(":"),
                    )
                    .bytes(hessid.to_vec())
                    .build(),
            );
        }

        fields
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
#[deku(bit_order = "lsb")]
pub struct AccessNetworkOptions {
    #[deku(bits = 4)]
    pub access_network_type: u8,
    #[deku(bits = 1)]
    pub internet: bool,
    #[deku(bits = 1)]
    pub asra: bool,
    #[deku(bits = 1)]
    pub esr: bool,
    #[deku(bits = 1)]
    pub uesa: bool,
}

impl AccessNetworkOptions {
    pub fn to_field(&self) -> Field {
        let byte = self
            .to_bytes()
            .unwrap_or_default()
            .first()
            .cloned()
            .unwrap_or_default();

        Field::builder()
            .title("Access Network Options")
            .value(format!("Type: {}", self.access_network_type))
            .byte(byte)
            .subfields([
                Field::builder()
                    .title("Access Network Type")
                    .value(self.access_network_type)
                    .bits(BitRange::new(&[byte], 0, 4))
                    .build(),
                Field::builder()
                    .title("Internet")
                    .value(self.internet)
                    .bits(BitRange::new(&[byte], 4, 1))
                    .build(),
                Field::builder()
                    .title("ASRA")
                    .value(self.asra)
                    .bits(BitRange::new(&[byte], 5, 1))
                    .build(),
                Field::builder()
                    .title("ESR")
                    .value(self.esr)
                    .bits(BitRange::new(&[byte], 6, 1))
                    .build(),
                Field::builder()
                    .title("UESA")
                    .value(self.uesa)
                    .bits(BitRange::new(&[byte], 7, 1))
                    .build(),
            ])
            .build()
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
pub struct VenueInfo {
    pub venue_group: VenueGroup,
    #[deku(bytes = 1)]
    pub venue_type: u8,
}

impl VenueInfo {
    pub fn to_field(&self) -> Field {
        let bytes = self.to_bytes().unwrap_or_default();

        Field::builder()
            .title("Venue Info")
            .value(format!("{} ({})", self.venue_group, self.venue_type))
            .bytes(bytes.clone())
            .subfields([
                Field::builder()
                    .title("Venue Group")
                    .value(self.venue_group)
                    .byte(self.venue_group as u8)
                    .build(),
                Field::builder()
                    .title("Venue Type")
                    .value(self.venue_type)
                    .byte(self.venue_type)
                    .build(),
            ])
            .build()
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
#[repr(u8)]
#[deku(id_type = "u8")]
pub enum VenueGroup {
    Unspecified = 0,
    Assembly,
    Business,
    Educational,
    FactoryAndIndustrial,
    Institutional,
    Mercantile,
    Residential,
    Storage,
    UtilityAndMiscellaneous,
    Vehicular,
    Outdoor,
    #[deku(id_pat = "_")]
    Reserved,
}

impl Display for VenueGroup {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Unspecified => write!(f, "Unspecified"),
            Self::Assembly => write!(f, "Assembly"),
            Self::Business => write!(f, "Business"),
            Self::Educational => write!(f, "Educational"),
            Self::FactoryAndIndustrial => write!(f, "Factory and Industrial"),
            Self::Institutional => write!(f, "Institutional"),
            Self::Mercantile => write!(f, "Mercantile"),
            Self::Residential => write!(f, "Residential"),
            Self::Storage => write!(f, "Storage"),
            Self::UtilityAndMiscellaneous => write!(f, "Utility and Miscellaneous"),
            Self::Vehicular => write!(f, "Vehicular"),
            Self::Outdoor => write!(f, "Outdoor"),
            Self::Reserved => write!(f, "Reserved"),
        }
    }
}