kawaiifi 0.2.0

Wi-Fi scanning library for Linux, macOS, and Windows.
Documentation
use deku::prelude::*;
use serde::{Deserialize, Serialize};

use super::{Field, IeData, IeId, elements::*};

/// A Wi-Fi Information Element.
///
/// Contains the element ID, length, optional extension ID, and parsed data.
#[derive(Clone, Debug, PartialEq, Eq, Hash, DekuRead, DekuWrite, Serialize, Deserialize)]
pub struct Ie {
    /// The element ID.
    pub id: u8,
    /// The length of the element data in bytes, excluding the ID and length fields.
    pub len: u8,
    /// The element extension ID, present only when `id` is 255.
    #[deku(cond = "*id == 255")]
    pub id_ext: Option<u8>,
    /// The parsed element data.
    #[deku(
        bytes = "Ie::compute_data_len(*len, *id_ext)?",
        ctx = "IeId { id: *id, id_ext: *id_ext }"
    )]
    pub data: IeData,
}

impl Ie {
    /// The human-readable name of this information element.
    pub const fn name(&self) -> &'static str {
        match self.data {
            IeData::AdvertisementProtocol(_) => AdvertisementProtocol::NAME,
            IeData::Antenna(_) => Antenna::NAME,
            IeData::ApChannelReport(_) => ApChannelReport::NAME,
            IeData::ApConfigurationSequenceNumber(_) => ApConfigurationSequenceNumber::NAME,
            IeData::AwakeWindow(_) => AwakeWindow::NAME,
            IeData::BssLoad(_) => BssLoad::NAME,
            IeData::ChallengeText(_) => ChallengeText::NAME,
            IeData::ChannelSwitchAnnouncement(_) => ChannelSwitchAnnouncement::NAME,
            IeData::Country(_) => Country::NAME,
            IeData::DsParameterSet(_) => DsParameterSet::NAME,
            IeData::EhtCapabilities(_) => EhtCapabilities::NAME,
            IeData::EhtOperation(_) => EhtOperation::NAME,
            IeData::ErpInfo(_) => ErpInfo::NAME,
            IeData::ExtendedCapabilities(_) => ExtendedCapabilities::NAME,
            IeData::ExtendedChannelSwitchAnnouncement(_) => ExtendedChannelSwitchAnnouncement::NAME,
            IeData::ExtendedSupportedRates(_) => ExtendedSupportedRates::NAME,
            IeData::FilsIndication(_) => FilsIndication::NAME,
            IeData::HeCapabilities(_) => HeCapabilities::NAME,
            IeData::HeOperation(_) => HeOperation::NAME,
            IeData::HeSixGhzBandCapabilities(_) => HeSixGhzBandCapabilities::NAME,
            IeData::HtCapabilities(_) => HtCapabilities::NAME,
            IeData::HtOperation(_) => HtOperation::NAME,
            IeData::IbssParameterSet(_) => IbssParameterSet::NAME,
            IeData::Interworking(_) => Interworking::NAME,
            IeData::MeasurementPilotTransmission(_) => MeasurementPilotTransmission::NAME,
            IeData::MeasurementRequest(_) => MeasurementRequest::NAME,
            IeData::MeshConfiguration(_) => MeshConfiguration::NAME,
            IeData::MeshId(_) => MeshId::NAME,
            IeData::MobilityDomain(_) => MobilityDomain::NAME,
            IeData::MuEdcaParameterSet(_) => MuEdcaParameterSet::NAME,
            IeData::OverlappingBssScanParams(_) => OverlappingBssScanParams::NAME,
            IeData::PowerCapability(_) => PowerCapability::NAME,
            IeData::PowerConstraint(_) => PowerConstraint::NAME,
            IeData::ReducedNeighborReport(_) => ReducedNeighborReport::NAME,
            IeData::RmEnabledCapabilities(_) => RmEnabledCapabilities::NAME,
            IeData::RoamingConsortium(_) => RoamingConsortium::NAME,
            IeData::Rsn(_) => Rsn::NAME,
            IeData::RsnExtension(_) => RsnExtension::NAME,
            IeData::SpatialReuseParameterSet(_) => SpatialReuseParameterSet::NAME,
            IeData::Ssid(_) => Ssid::NAME,
            IeData::SupportedOperatingClasses(_) => SupportedOperatingClasses::NAME,
            IeData::SupportedRates(_) => SupportedRates::NAME,
            IeData::Tim(_) => Tim::NAME,
            IeData::TimeAdvertisement(_) => TimeAdvertisement::NAME,
            IeData::TimeZone(_) => TimeZone::NAME,
            IeData::TpcReport(_) => TpcReport::NAME,
            IeData::TransmitPowerEnvelope(_) => TransmitPowerEnvelope::NAME,
            IeData::TwentyFortyBssCoexistence(_) => TwentyFortyBssCoexistence::NAME,
            IeData::TwentyFortyBssIntolerantChannelReport(_) => {
                TwentyFortyBssIntolerantChannelReport::NAME
            }
            IeData::VendorSpecific(_) => VendorSpecific::NAME,
            IeData::VhtCapabilities(_) => VhtCapabilities::NAME,
            IeData::VhtOperation(_) => VhtOperation::NAME,
            IeData::Unknown { .. } => Unknown::NAME,
        }
    }

    /// The raw bytes of this information element, including the ID, length, and data fields.
    pub fn bytes(&self) -> Vec<u8> {
        self.to_bytes().unwrap_or_default()
    }

    /// A short human-readable summary of the element's contents.
    pub fn summary(&self) -> String {
        match &self.data {
            IeData::AdvertisementProtocol(data) => data.summary(),
            IeData::Antenna(data) => data.summary(),
            IeData::ApChannelReport(data) => data.summary(),
            IeData::ApConfigurationSequenceNumber(data) => data.summary(),
            IeData::AwakeWindow(data) => data.summary(),
            IeData::BssLoad(data) => data.summary(),
            IeData::ChallengeText(data) => data.summary(),
            IeData::ChannelSwitchAnnouncement(data) => data.summary(),
            IeData::Country(data) => data.summary(),
            IeData::DsParameterSet(data) => data.summary(),
            IeData::EhtCapabilities(data) => data.summary(),
            IeData::EhtOperation(data) => data.summary(),
            IeData::ErpInfo(data) => data.summary(),
            IeData::ExtendedCapabilities(data) => data.summary(),
            IeData::ExtendedChannelSwitchAnnouncement(data) => data.summary(),
            IeData::ExtendedSupportedRates(data) => data.summary(),
            IeData::FilsIndication(data) => data.summary(),
            IeData::HeCapabilities(data) => data.summary(),
            IeData::HeOperation(data) => data.summary(),
            IeData::HeSixGhzBandCapabilities(data) => data.summary(),
            IeData::HtCapabilities(data) => data.summary(),
            IeData::HtOperation(data) => data.summary(),
            IeData::IbssParameterSet(data) => data.summary(),
            IeData::Interworking(data) => data.summary(),
            IeData::MeasurementPilotTransmission(data) => data.summary(),
            IeData::MeasurementRequest(data) => data.summary(),
            IeData::MeshConfiguration(data) => data.summary(),
            IeData::MeshId(data) => data.summary(),
            IeData::MobilityDomain(data) => data.summary(),
            IeData::MuEdcaParameterSet(data) => data.summary(),
            IeData::OverlappingBssScanParams(data) => data.summary(),
            IeData::PowerCapability(data) => data.summary(),
            IeData::PowerConstraint(data) => data.summary(),
            IeData::ReducedNeighborReport(data) => data.summary(),
            IeData::RmEnabledCapabilities(data) => data.summary(),
            IeData::RoamingConsortium(data) => data.summary(),
            IeData::Rsn(data) => data.summary(),
            IeData::RsnExtension(data) => data.summary(),
            IeData::SpatialReuseParameterSet(data) => data.summary(),
            IeData::Ssid(data) => data.summary(),
            IeData::SupportedOperatingClasses(data) => data.summary(),
            IeData::SupportedRates(data) => data.summary(),
            IeData::Tim(data) => data.summary(),
            IeData::TimeAdvertisement(data) => data.summary(),
            IeData::TimeZone(data) => data.summary(),
            IeData::TpcReport(data) => data.summary(),
            IeData::TransmitPowerEnvelope(data) => data.summary(),
            IeData::TwentyFortyBssCoexistence(data) => data.summary(),
            IeData::TwentyFortyBssIntolerantChannelReport(data) => data.summary(),
            IeData::VendorSpecific(data) => data.summary(),
            IeData::VhtCapabilities(data) => data.summary(),
            IeData::VhtOperation(data) => data.summary(),
            IeData::Unknown { unknown, .. } => unknown.summary(),
        }
    }

    /// The parsed fields of this information element.
    pub fn fields(&self) -> Vec<Field> {
        match &self.data {
            IeData::AdvertisementProtocol(data) => data.fields(),
            IeData::Antenna(data) => data.fields(),
            IeData::ApChannelReport(data) => data.fields(),
            IeData::ApConfigurationSequenceNumber(data) => data.fields(),
            IeData::AwakeWindow(data) => data.fields(),
            IeData::BssLoad(data) => data.fields(),
            IeData::ChallengeText(data) => data.fields(),
            IeData::ChannelSwitchAnnouncement(data) => data.fields(),
            IeData::Country(data) => data.fields(),
            IeData::DsParameterSet(data) => data.fields(),
            IeData::EhtCapabilities(data) => data.fields(),
            IeData::EhtOperation(data) => data.fields(),
            IeData::ErpInfo(data) => data.fields(),
            IeData::ExtendedCapabilities(data) => data.fields(),
            IeData::ExtendedChannelSwitchAnnouncement(data) => data.fields(),
            IeData::ExtendedSupportedRates(data) => data.fields(),
            IeData::FilsIndication(data) => data.fields(),
            IeData::HeCapabilities(data) => data.fields(),
            IeData::HeOperation(data) => data.fields(),
            IeData::HeSixGhzBandCapabilities(data) => data.fields(),
            IeData::HtCapabilities(data) => data.fields(),
            IeData::HtOperation(data) => data.fields(),
            IeData::IbssParameterSet(data) => data.fields(),
            IeData::Interworking(data) => data.fields(),
            IeData::MeasurementPilotTransmission(data) => data.fields(),
            IeData::MeasurementRequest(data) => data.fields(),
            IeData::MeshConfiguration(data) => data.fields(),
            IeData::MeshId(data) => data.fields(),
            IeData::MobilityDomain(data) => data.fields(),
            IeData::MuEdcaParameterSet(data) => data.fields(),
            IeData::OverlappingBssScanParams(data) => data.fields(),
            IeData::PowerCapability(data) => data.fields(),
            IeData::PowerConstraint(data) => data.fields(),
            IeData::ReducedNeighborReport(data) => data.fields(),
            IeData::RmEnabledCapabilities(data) => data.fields(),
            IeData::RoamingConsortium(data) => data.fields(),
            IeData::Rsn(data) => data.fields(),
            IeData::RsnExtension(data) => data.fields(),
            IeData::SpatialReuseParameterSet(data) => data.fields(),
            IeData::Ssid(data) => data.fields(),
            IeData::SupportedOperatingClasses(data) => data.fields(),
            IeData::SupportedRates(data) => data.fields(),
            IeData::Tim(data) => data.fields(),
            IeData::TimeAdvertisement(data) => data.fields(),
            IeData::TimeZone(data) => data.fields(),
            IeData::TpcReport(data) => data.fields(),
            IeData::TransmitPowerEnvelope(data) => data.fields(),
            IeData::TwentyFortyBssCoexistence(data) => data.fields(),
            IeData::TwentyFortyBssIntolerantChannelReport(data) => data.fields(),
            IeData::VendorSpecific(data) => data.fields(),
            IeData::VhtCapabilities(data) => data.fields(),
            IeData::VhtOperation(data) => data.fields(),
            IeData::Unknown { unknown, .. } => unknown.fields(),
        }
    }

    fn compute_data_len(len: u8, id_ext: Option<u8>) -> Result<usize, DekuError> {
        if id_ext.is_some() {
            (usize::from(len)).checked_sub(1).ok_or_else(|| {
                DekuError::Assertion("IE with ID extension has len=0 (invalid)".into())
            })
        } else {
            Ok(usize::from(len))
        }
    }
}