use std::fmt;
use busylib::model::wifi::{
StatusResponse, WifiIpMethod, WifiIpType, WifiSecurityMethod, WifiState,
};
use serde::Serialize;
use crate::reporter::events::CliEvent;
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct WifiStatusEvent(StatusResponse);
impl WifiStatusEvent {
pub fn new(status: StatusResponse) -> Self {
Self(status)
}
}
impl From<WifiStatusEvent> for CliEvent {
fn from(event: WifiStatusEvent) -> Self {
CliEvent::WifiStatus(event)
}
}
impl fmt::Display for WifiStatusEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let status = &self.0;
write!(f, "state: {}", state_label(&status.state))?;
if let Some(ssid) = &status.ssid {
write!(f, "\nssid: {ssid}")?;
}
if let Some(bssid) = &status.bssid {
write!(f, "\nbssid: {bssid}")?;
}
if let Some(channel) = status.channel {
write!(f, "\nchannel: {channel}")?;
}
if let Some(rssi) = status.rssi {
write!(f, "\nrssi: {rssi} dBm")?;
}
if let Some(security) = &status.security {
write!(f, "\nsecurity: {}", security_label(security))?;
}
if let Some(ip_config) = &status.ip_config {
if let Some(ip_method) = ip_config.ip_method {
write!(f, "\nip method: {}", ip_method_label(ip_method))?;
}
if let Some(ip_type) = ip_config.ip_type {
write!(f, "\nip type: {}", ip_type_label(ip_type))?;
}
if let Some(address) = &ip_config.address {
write!(f, "\naddress: {address}")?;
}
}
Ok(())
}
}
fn state_label(state: &WifiState) -> &str {
match state {
WifiState::Unknown => "unknown",
WifiState::Disconnected => "disconnected",
WifiState::Connected => "connected",
WifiState::Connecting => "connecting",
WifiState::Disconnecting => "disconnecting",
WifiState::Reconnecting => "reconnecting",
WifiState::Unrecognized(state) => state,
}
}
fn security_label(security: &WifiSecurityMethod) -> &str {
match security {
WifiSecurityMethod::Open => "Open",
WifiSecurityMethod::Wpa => "WPA",
WifiSecurityMethod::Wpa2 => "WPA2",
WifiSecurityMethod::Wep => "WEP",
WifiSecurityMethod::WpaWpa2 => "WPA/WPA2",
WifiSecurityMethod::Wpa3 => "WPA3",
WifiSecurityMethod::Wpa2Wpa3 => "WPA2/WPA3",
WifiSecurityMethod::Unsupported => "Unsupported",
WifiSecurityMethod::Unknown(security) => security,
}
}
fn ip_method_label(ip_method: WifiIpMethod) -> &'static str {
match ip_method {
WifiIpMethod::Dhcp => "dhcp",
WifiIpMethod::Static => "static",
}
}
fn ip_type_label(ip_type: WifiIpType) -> &'static str {
match ip_type {
WifiIpType::Ipv4 => "ipv4",
WifiIpType::Ipv6 => "ipv6",
}
}
#[cfg(test)]
mod tests {
use busylib::model::wifi::WifiIpConfig;
use serde_json::json;
use super::*;
fn connected() -> WifiStatusEvent {
WifiStatusEvent::new(StatusResponse {
state: WifiState::Connected,
ssid: Some("busy-network".to_string()),
bssid: Some("a1:b2:c3:d4:e5:f6".to_string()),
channel: Some(11),
rssi: Some(-57),
security: Some(WifiSecurityMethod::Wpa2Wpa3),
ip_config: Some(WifiIpConfig {
ip_method: Some(WifiIpMethod::Dhcp),
ip_type: Some(WifiIpType::Ipv4),
address: Some("10.0.4.20".to_string()),
}),
})
}
fn disconnected() -> WifiStatusEvent {
WifiStatusEvent::new(StatusResponse {
state: WifiState::Disconnected,
ssid: None,
bssid: None,
channel: None,
rssi: None,
security: None,
ip_config: None,
})
}
fn json_of(event: WifiStatusEvent) -> serde_json::Value {
serde_json::to_value(CliEvent::from(event)).unwrap()
}
#[test]
fn renders_a_connected_status_as_text() {
assert_eq!(
connected().to_string(),
"state: connected\n\
ssid: busy-network\n\
bssid: a1:b2:c3:d4:e5:f6\n\
channel: 11\n\
rssi: -57 dBm\n\
security: WPA2/WPA3\n\
ip method: dhcp\n\
ip type: ipv4\n\
address: 10.0.4.20"
);
}
#[test]
fn leaves_out_fields_that_are_absent_while_disconnected() {
assert_eq!(disconnected().to_string(), "state: disconnected");
}
#[test]
fn renders_states_and_security_methods_the_way_the_api_spells_them() {
let unrecognized = WifiStatusEvent::new(StatusResponse {
state: WifiState::Unrecognized("rebooting".to_string()),
security: Some(WifiSecurityMethod::Unknown("WPA4".to_string())),
..disconnected().0
});
assert_eq!(unrecognized.to_string(), "state: rebooting\nsecurity: WPA4");
assert_eq!(
json_of(unrecognized),
json!({
"event": "wifi_status",
"state": "rebooting",
"ssid": null,
"bssid": null,
"channel": null,
"rssi": null,
"security": "WPA4",
"ip_config": null
})
);
}
#[test]
fn renders_a_connected_status_as_json() {
assert_eq!(
json_of(connected()),
json!({
"event": "wifi_status",
"state": "connected",
"ssid": "busy-network",
"bssid": "a1:b2:c3:d4:e5:f6",
"channel": 11,
"rssi": -57,
"security": "WPA2/WPA3",
"ip_config": {
"ip_method": "dhcp",
"ip_type": "ipv4",
"address": "10.0.4.20"
}
})
);
}
#[test]
fn renders_a_disconnected_status_as_json() {
assert_eq!(
json_of(disconnected()),
json!({
"event": "wifi_status",
"state": "disconnected",
"ssid": null,
"bssid": null,
"channel": null,
"rssi": null,
"security": null,
"ip_config": null
})
);
}
}