use alloc::string::String;
use core::fmt;
use procmacros::BuilderLite;
#[cfg(feature = "unstable")]
use super::CountryInfo;
use super::{AuthenticationMethod, Protocols, SecondaryChannel, Ssid};
use crate::{WifiError, sys::include::wifi_ap_record_t};
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct AccessPointInfo {
pub ssid: Ssid,
pub bssid: [u8; 6],
pub channel: u8,
pub secondary_channel: SecondaryChannel,
pub signal_strength: i8,
pub auth_method: Option<AuthenticationMethod>,
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
pub country: Option<CountryInfo>,
}
#[derive(Clone, PartialEq, Eq, BuilderLite, Hash)]
pub struct AccessPointConfig {
#[builder_lite(skip_setter)]
pub(crate) ssid: Ssid,
pub(crate) ssid_hidden: bool,
pub(crate) channel: u8,
pub(crate) secondary_channel: Option<SecondaryChannel>,
pub(crate) protocols: Protocols,
pub(crate) auth_method: AuthenticationMethod,
#[builder_lite(reference)]
pub(crate) password: String,
pub(crate) max_connections: u16,
pub(crate) dtim_period: u8,
#[builder_lite(unstable)]
pub(crate) beacon_timeout: u16,
}
impl AccessPointConfig {
pub fn with_ssid(mut self, ssid: impl Into<Ssid>) -> Self {
self.ssid = ssid.into();
self
}
pub(crate) fn validate(&self) -> Result<(), WifiError> {
if self.ssid.len() > 32 {
return Err(WifiError::InvalidArguments);
}
if self.password.len() > 64 {
return Err(WifiError::InvalidArguments);
}
if !(1..=10).contains(&self.dtim_period) {
return Err(WifiError::InvalidArguments);
}
Ok(())
}
}
impl Default for AccessPointConfig {
fn default() -> Self {
Self {
ssid: "iot-device".into(),
ssid_hidden: false,
channel: 1,
secondary_channel: None,
protocols: Protocols::default(),
auth_method: AuthenticationMethod::None,
password: String::new(),
max_connections: 255,
dtim_period: 2,
beacon_timeout: 300,
}
}
}
impl fmt::Debug for AccessPointConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AccessPointConfig")
.field("ssid", &self.ssid)
.field("ssid_hidden", &self.ssid_hidden)
.field("channel", &self.channel)
.field("secondary_channel", &self.secondary_channel)
.field("protocols", &self.protocols)
.field("auth_method", &self.auth_method)
.field("password", &"**REDACTED**")
.field("max_connections", &self.max_connections)
.field("dtim_period", &self.dtim_period)
.field("beacon_timeout", &self.beacon_timeout)
.finish()
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for AccessPointConfig {
fn format(&self, fmt: defmt::Formatter<'_>) {
defmt::write!(
fmt,
"AccessPointConfig {{\
ssid: {}, \
ssid_hidden: {}, \
channel: {}, \
secondary_channel: {}, \
protocols: {}, \
auth_method: {}, \
password: **REDACTED**, \
max_connections: {}, \
dtim_period: {}, \
beacon_timeout: {} \
}}",
self.ssid.as_str(),
self.ssid_hidden,
self.channel,
self.secondary_channel,
self.protocols,
self.auth_method,
self.max_connections,
self.dtim_period,
self.beacon_timeout
);
}
}
#[allow(non_upper_case_globals)]
pub(crate) fn convert_ap_info(record: &wifi_ap_record_t) -> AccessPointInfo {
let str_len = record
.ssid
.iter()
.position(|&c| c == 0)
.unwrap_or(record.ssid.len());
let ssid = Ssid::from(&record.ssid[..str_len]);
AccessPointInfo {
ssid,
bssid: record.bssid,
channel: record.primary,
secondary_channel: SecondaryChannel::from_raw(record.second),
signal_strength: record.rssi,
auth_method: Some(AuthenticationMethod::from_raw(record.authmode)),
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
country: CountryInfo::try_from_c(&record.country),
}
}