diff --git a/packages/client-api/src/providers/network/create-network-provider.ts b/packages/client-api/src/providers/network/create-network-provider.ts
index cda77780..3d854538 100644
--- a/packages/client-api/src/providers/network/create-network-provider.ts
+++ b/packages/client-api/src/providers/network/create-network-provider.ts
@@ -4,11 +4,52 @@ import type { NetworkProviderConfig } from '~/user-config';
import { createProviderListener } from '../create-provider-listener';
export interface NetworkVariables {
- defaultInterface: NetworkInterface;
- defaultGateway: Gateway;
+ defaultInterface: NetworkInterface | null;
+ defaultGateway: NetworkGateway | null;
interfaces: NetworkInterface[];
}
+export interface NetworkInterface {
+ name: string;
+ friendlyName: string | null;
+ description: string | null;
+ type: InterfaceType;
+ ipv4Addresses: string[];
+ ipv6Addresses: string[];
+ macAddress: string | null;
+ transmitSeed: number | null;
+ receiveSpeed: number | null;
+ dnsServers: string[];
+ isDefault: boolean;
+}
+
+export interface NetworkGateway {
+ macAddress: string;
+ ipv4Addresses: string[];
+ ipv6Addresses: string[];
+ ssid: string | null;
+ signalStrength: number | null;
+}
+
+export enum InterfaceType {
+ UNKNOWN = 'unknown',
+ ETHERNET = 'ethernet',
+ TOKEN_RING = 'token_ring',
+ FDDI = 'fddi',
+ PPP = 'ppp',
+ LOOPBACK = 'loopback',
+ SLIP = 'slip',
+ ATM = 'atm',
+ GENERIC_MODEM = 'generic_modem',
+ ISDN = 'isdn',
+ WIFI = 'wifi',
+ DSL = 'dsl',
+ TUNNEL = 'tunnel',
+ HIGH_PERFORMANCE_SERIAL_BUS = 'high_performance_serial_bus',
+ MOBILE_BROADBAND = 'mobile_broadband',
+ BRIDGE = 'bridge',
+}
+
export async function createNetworkProvider(
config: NetworkProviderConfig,
owner: Owner,
@@ -30,82 +71,3 @@ export async function createNetworkProvider(
},
};
}
-
-export interface NetworkInterface {
- name: string;
- friendlName: string;
- description: string;
- interfaceType: InterfaceType;
- ipv4: Ipv4Net;
- ipv6: Ipv6Net;
- macAddress: MacAddress;
- transmitSeed: number;
- receiveSpeed: number;
- dnsServers: (Ipv4Addr | Ipv6Addr)[];
- default: boolean;
-}
-
-export interface Gateway {
- macAddress: MacAddress;
- ipv4: Ipv4Addr[];
- ipv6: Ipv6Addr[];
- ssid: string;
- signal_strength: number;
- connected: boolean;
-}
-
-enum InterfaceType {
- Unknown,
- Ethernet,
- TokenRing,
- Fddi,
- BasicIsdn,
- PrimaryIsdn,
- Ppp,
- Loopback,
- Ethernet3Megabit,
- Slip,
- Atm,
- GenericModem,
- FastEthernetT,
- Isdn,
- FastEthernetFx,
- Wireless80211,
- AsymmetricDsl,
- RateAdaptDsl,
- SymmetricDsl,
- VeryHighSpeedDsl,
- IPOverAtm,
- GigabitEthernet,
- Tunnel,
- MultiRateSymmetricDsl,
- HighPerformanceSerialBus,
- Wman,
- Wwanpp,
- Wwanpp2,
- Bridge,
-}
-
-interface Ipv4Net {
- addr: Ipv4Addr;
- netmask: Ipv4Addr;
- prefixLength: number;
-}
-
-interface Ipv6Net {
- addr: Ipv6Addr;
- netmask: Ipv6Addr;
- prefixLength: number;
-}
-
-interface Ipv6Addr {
- octects: number[];
-}
-
-interface Ipv4Addr {
- octects: number[];
-}
-
-interface MacAddress {
- octects: number[];
-}
diff --git a/packages/desktop/resources/sample-config.yaml b/packages/desktop/resources/sample-config.yaml
index a3b35b15..5934a70c 100644
--- a/packages/desktop/resources/sample-config.yaml
+++ b/packages/desktop/resources/sample-config.yaml
@@ -108,18 +108,19 @@ window/bar:
template/network:
providers: ['network']
template: |
-
- @if (network.defaultInterface.type === 'Ethernet') {
- @if (network.defaultGateway.isConnected !== true) { }
- @else { }
+ @if (network.defaultInterface?.type === 'ethernet') {
+
+ } @else if (network.defaultInterface?.type === 'wifi') {
+ @if (network.defaultGateway?.signalStrength >= 80) { }
+ @else if (network.defaultGateway?.signalStrength >= 65) { }
+ @else if (network.defaultGateway?.signalStrength >= 40) { }
+ @else if (network.defaultGateway?.signalStrength >= 25) { }
+ @else { }
+ {{ network.defaultGateway?.ssid }}
+ } @else {
+
}
- @else if (network.defaultGateway.isConnected !== true) { }
- @else if (network.defaultGateway.signalStrengthPercent >= 80) { }
- @else if (network.defaultGateway.signalStrengthPercent >= 65) { }
- @else if (network.defaultGateway.signalStrengthPercent >= 40) { }
- @else if (network.defaultGateway.signalStrengthPercent >= 25) { }
- @else { }
template/memory:
providers: ['memory']
diff --git a/packages/desktop/src/providers/network/mod.rs b/packages/desktop/src/providers/network/mod.rs
index a4831acf..a4303490 100644
--- a/packages/desktop/src/providers/network/mod.rs
+++ b/packages/desktop/src/providers/network/mod.rs
@@ -1,7 +1,7 @@
mod config;
mod provider;
mod variables;
-mod parse_netsh;
+mod wifi_hotspot;
pub use config::*;
pub use provider::*;
diff --git a/packages/desktop/src/providers/network/parse_netsh.rs b/packages/desktop/src/providers/network/parse_netsh.rs
deleted file mode 100644
index 6b808db2..00000000
--- a/packages/desktop/src/providers/network/parse_netsh.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-use anyhow::Context;
-use regex::Regex;
-
-// Runs the netsh command and parses the output
-// to get the primary interface's SSID and signal strength.
-//
-// Unclear if the primary interface is always the default interface
-// returned by the netdev/defaultnet crate - requires more testing.
-
-#[derive(Debug)]
-pub struct NetshInfo {
- pub ssid: Option,
- pub signal: Option,
- pub connected: bool,
-}
-
-#[cfg(target_os = "windows")]
-pub fn get_primary_interface_ssid_and_strength(
-) -> anyhow::Result {
- let ssid_match = Regex::new(r"(?m)^\s*SSID\s*:\s*(.*?)\r?$").unwrap();
-
- let signal_match =
- Regex::new(r"(?m)^\s*Signal\s*:\s*(.*?)\r?$").unwrap();
-
- let signal_strip = Regex::new(r"(\d+)%").unwrap();
-
- let connected_match =
- Regex::new(r"(?m)^\s*State\s*:\s*(.*?)\r?$").unwrap();
-
- let output = std::process::Command::new("netsh")
- .args(&["wlan", "show", "interfaces"])
- .output()
- .context("could not run netsh")?;
-
- let output = String::from_utf8_lossy(&output.stdout);
-
- let ssid = ssid_match
- .captures(&output)
- .map(|m| m.get(1).unwrap().as_str().to_string());
- let signal_str: Option<&str> = signal_match
- .captures(&output)
- .map(|m| m.get(1).unwrap().as_str());
- let signal: Option = signal_str
- .and_then(|s| signal_strip.captures(s))
- .map(|m| m.get(1).unwrap().as_str().parse().unwrap());
- let connected: bool = connected_match
- .captures(&output)
- .map(|m| m.get(1).unwrap().as_str().to_string()) == Some("connected".to_string());
-
- return Ok(NetshInfo { ssid, signal, connected });
-}
-
-#[cfg(not(target_os = "windows"))]
-pub fn get_primary_interface_ssid_and_strength(
-) -> anyhow::Result {
- Ok(NetshInfo {
- ssid: None,
- signal: None,
- connected: false,
- })
-}
diff --git a/packages/desktop/src/providers/network/provider.rs b/packages/desktop/src/providers/network/provider.rs
index 38fa20d5..a55887bb 100644
--- a/packages/desktop/src/providers/network/provider.rs
+++ b/packages/desktop/src/providers/network/provider.rs
@@ -2,19 +2,16 @@ use std::sync::Arc;
use anyhow::Result;
use async_trait::async_trait;
-use netdev::interface::{
- get_default_interface, get_interfaces,
-};
use tokio::{sync::Mutex, task::AbortHandle};
use crate::providers::{
- interval_provider::IntervalProvider,
- network::parse_netsh::get_primary_interface_ssid_and_strength,
- variables::ProviderVariables,
+ interval_provider::IntervalProvider, variables::ProviderVariables,
};
use super::{
- Gateway, NetworkInterface, NetworkProviderConfig, NetworkVariables,
+ wifi_hotspot::{default_gateway_wifi, WifiHotstop},
+ InterfaceType, NetworkGateway, NetworkInterface, NetworkProviderConfig,
+ NetworkVariables,
};
pub struct NetworkProvider {
@@ -31,6 +28,57 @@ impl NetworkProvider {
_state: Arc::new(Mutex::new(())),
}
}
+
+ fn transform_interface(
+ interface: &netdev::Interface,
+ ) -> NetworkInterface {
+ NetworkInterface {
+ name: interface.name.to_string(),
+ friendly_name: interface.friendly_name.clone(),
+ description: interface.description.clone(),
+ interface_type: InterfaceType::from(interface.if_type),
+ ipv4_addresses: interface
+ .ipv4
+ .iter()
+ .map(|ip| ip.to_string())
+ .collect(),
+ ipv6_addresses: interface
+ .ipv6
+ .iter()
+ .map(|ip| ip.to_string())
+ .collect(),
+ mac_address: interface.mac_addr.map(|mac| mac.to_string()),
+ transmit_speed: interface.transmit_speed,
+ receive_speed: interface.receive_speed,
+ dns_servers: interface
+ .dns_servers
+ .iter()
+ .map(|ip| ip.to_string())
+ .collect(),
+ is_default: interface.default,
+ }
+ }
+
+ fn transform_gateway(
+ gateway: &netdev::NetworkDevice,
+ wifi_hotspot: WifiHotstop,
+ ) -> NetworkGateway {
+ NetworkGateway {
+ mac_address: gateway.mac_addr.address(),
+ ipv4_addresses: gateway
+ .ipv4
+ .iter()
+ .map(|ip| ip.to_string())
+ .collect(),
+ ipv6_addresses: gateway
+ .ipv6
+ .iter()
+ .map(|ip| ip.to_string())
+ .collect(),
+ ssid: wifi_hotspot.ssid,
+ signal_strength: wifi_hotspot.signal_strength,
+ }
+ }
}
#[async_trait]
@@ -58,67 +106,24 @@ impl IntervalProvider for NetworkProvider {
_: &NetworkProviderConfig,
_state: &(),
) -> Result {
- let default_interface = get_default_interface().unwrap();
-
- let interfaces = get_interfaces();
+ let interfaces = netdev::get_interfaces();
- let default_gateway_ssid_and_strength =
- get_primary_interface_ssid_and_strength()?; // Returns ssid = None, signal = None, connected = false if not on Windows for now
+ let default_interface = netdev::get_default_interface().ok();
let variables = NetworkVariables {
- default_interface: NetworkInterface {
- name: default_interface.name.clone(),
- friendly_name: default_interface.friendly_name.clone(),
- description: default_interface.description.clone(),
- interface_type: default_interface.if_type,
- ipv4: default_interface.ipv4.clone(),
- ipv6: default_interface.ipv6.clone(),
- mac_address: default_interface.mac_addr.unwrap(),
- transmit_speed: default_interface.transmit_speed,
- receive_speed: default_interface.receive_speed,
- dns_servers: default_interface.dns_servers.clone(),
- is_default: default_interface.default,
- },
- default_gateway: Gateway {
- mac_address: default_interface
- .gateway
- .as_ref()
- .unwrap()
- .mac_addr
- .clone(),
- ipv4_addresses: default_interface
- .gateway
- .as_ref()
- .unwrap()
- .ipv4
- .clone(),
- ipv6_addresses: default_interface
- .gateway
- .as_ref()
- .unwrap()
- .ipv6
- .clone(),
- ssid: default_gateway_ssid_and_strength.ssid.unwrap(),
- signal_strength: default_gateway_ssid_and_strength
- .signal
- .unwrap(),
- is_connected: default_gateway_ssid_and_strength.connected,
- },
+ default_interface: default_interface
+ .as_ref()
+ .map(Self::transform_interface),
+ default_gateway: default_interface
+ .and_then(|interface| interface.gateway)
+ .and_then(|gateway| {
+ default_gateway_wifi()
+ .map(|wifi| Self::transform_gateway(&gateway, wifi))
+ .ok()
+ }),
interfaces: interfaces
.iter()
- .map(|iface| NetworkInterface {
- name: iface.name.clone(),
- friendly_name: iface.friendly_name.clone(),
- description: iface.description.clone(),
- interface_type: iface.if_type.clone(),
- ipv4: iface.ipv4.clone(),
- ipv6: iface.ipv6.clone(),
- mac_address: iface.mac_addr.unwrap().clone(),
- transmit_speed: iface.transmit_speed,
- receive_speed: iface.receive_speed,
- dns_servers: iface.dns_servers.clone(),
- is_default: iface.default,
- })
+ .map(Self::transform_interface)
.collect(),
};
diff --git a/packages/desktop/src/providers/network/variables.rs b/packages/desktop/src/providers/network/variables.rs
index 1d9042aa..f8070fe3 100644
--- a/packages/desktop/src/providers/network/variables.rs
+++ b/packages/desktop/src/providers/network/variables.rs
@@ -1,14 +1,11 @@
use netdev::interface::InterfaceType as NdInterfaceType;
-use netdev::ip::{Ipv4Net as NdIpv4Net, Ipv6Net as NdIpv6Net};
-use netdev::mac::MacAddr as NdMacAddr;
-use serde::{Serialize, Serializer};
-use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+use serde::Serialize;
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct NetworkVariables {
- pub default_interface: NetworkInterface,
- pub default_gateway: Gateway,
+ pub default_interface: Option,
+ pub default_gateway: Option,
pub interfaces: Vec,
}
@@ -18,203 +15,83 @@ pub struct NetworkInterface {
pub name: String,
pub friendly_name: Option,
pub description: Option,
- #[serde(serialize_with = "interfacetype_ser")]
#[serde(rename = "type")]
- pub interface_type: NdInterfaceType,
- #[serde(serialize_with = "ipv4_ser")]
- pub ipv4: Vec,
- #[serde(serialize_with = "ipv6_ser")]
- pub ipv6: Vec,
- #[serde(serialize_with = "macaddr_ser")]
- pub mac_address: NdMacAddr,
+ pub interface_type: InterfaceType,
+ pub ipv4_addresses: Vec,
+ pub ipv6_addresses: Vec,
+ pub mac_address: Option,
pub transmit_speed: Option,
pub receive_speed: Option,
- pub dns_servers: Vec,
+ pub dns_servers: Vec,
pub is_default: bool,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
-pub struct Gateway {
- #[serde(serialize_with = "macaddr_ser")]
- pub mac_address: NdMacAddr,
- pub ipv4_addresses: Vec,
- pub ipv6_addresses: Vec,
- pub ssid: String,
- pub signal_strength: u32,
- pub is_connected: bool,
+pub struct NetworkGateway {
+ pub mac_address: String,
+ pub ipv4_addresses: Vec,
+ pub ipv6_addresses: Vec,
+ pub ssid: Option,
+ pub signal_strength: Option,
}
#[derive(Serialize, Debug, Clone)]
-#[serde(rename_all = "camelCase")]
+#[serde(rename_all = "snake_case")]
pub enum InterfaceType {
Unknown,
Ethernet,
TokenRing,
Fddi,
- BasicIsdn,
- PrimaryIsdn,
Ppp,
Loopback,
- Ethernet3Megabit,
Slip,
Atm,
GenericModem,
- FastEthernetT,
Isdn,
- FastEthernetFx,
- Wireless80211,
- AsymmetricDsl,
- RateAdaptDsl,
- SymmetricDsl,
- VeryHighSpeedDsl,
- IPOverAtm,
- GigabitEthernet,
+ Wifi,
+ Dsl,
Tunnel,
- MultiRateSymmetricDsl,
HighPerformanceSerialBus,
- Wman,
- Wwanpp,
- Wwanpp2,
+ MobileBroadband,
Bridge,
}
-fn itype_to_local(itype: &NdInterfaceType) -> InterfaceType {
- match itype {
- NdInterfaceType::Unknown => InterfaceType::Unknown,
- NdInterfaceType::Ethernet => InterfaceType::Ethernet,
- NdInterfaceType::TokenRing => InterfaceType::TokenRing,
- NdInterfaceType::Fddi => InterfaceType::Fddi,
- NdInterfaceType::BasicIsdn => InterfaceType::BasicIsdn,
- NdInterfaceType::PrimaryIsdn => InterfaceType::PrimaryIsdn,
- NdInterfaceType::Ppp => InterfaceType::Ppp,
- NdInterfaceType::Loopback => InterfaceType::Loopback,
- NdInterfaceType::Ethernet3Megabit => InterfaceType::Ethernet3Megabit,
- NdInterfaceType::Slip => InterfaceType::Slip,
- NdInterfaceType::Atm => InterfaceType::Atm,
- NdInterfaceType::GenericModem => InterfaceType::GenericModem,
- NdInterfaceType::FastEthernetT => InterfaceType::FastEthernetT,
- NdInterfaceType::Isdn => InterfaceType::Isdn,
- NdInterfaceType::FastEthernetFx => InterfaceType::FastEthernetFx,
- NdInterfaceType::Wireless80211 => InterfaceType::Wireless80211,
- NdInterfaceType::AsymmetricDsl => InterfaceType::AsymmetricDsl,
- NdInterfaceType::RateAdaptDsl => InterfaceType::RateAdaptDsl,
- NdInterfaceType::SymmetricDsl => InterfaceType::SymmetricDsl,
- NdInterfaceType::VeryHighSpeedDsl => InterfaceType::VeryHighSpeedDsl,
- NdInterfaceType::IPOverAtm => InterfaceType::IPOverAtm,
- NdInterfaceType::GigabitEthernet => InterfaceType::GigabitEthernet,
- NdInterfaceType::Tunnel => InterfaceType::Tunnel,
- NdInterfaceType::MultiRateSymmetricDsl => {
- InterfaceType::MultiRateSymmetricDsl
- }
- NdInterfaceType::HighPerformanceSerialBus => {
- InterfaceType::HighPerformanceSerialBus
- }
- NdInterfaceType::Wman => InterfaceType::Wman,
- NdInterfaceType::Wwanpp => InterfaceType::Wwanpp,
- NdInterfaceType::Wwanpp2 => InterfaceType::Wwanpp2,
- NdInterfaceType::Bridge => InterfaceType::Bridge,
- }
-}
-
-fn interfacetype_ser(
- itype: &NdInterfaceType,
- serializer: S,
-) -> Result {
- let local_itype = itype_to_local(itype);
- local_itype.serialize(serializer)
-}
-
-#[derive(Serialize, Debug, Clone)]
-#[serde(rename_all = "camelCase")]
-pub struct Ipv4Net {
- pub address: Ipv4Addr,
- pub prefix_length: u8,
- pub netmask: Ipv4Addr,
-}
-
-fn ipv4net_to_local(ipv4net: &Vec) -> Vec {
- let mut result = Vec::new();
- for net in ipv4net {
- match net {
- NdIpv4Net {
- addr,
- prefix_len,
- netmask,
- } => {
- result.push(Ipv4Net {
- address: *addr,
- prefix_length: *prefix_len,
- netmask: *netmask,
- });
+impl From for InterfaceType {
+ fn from(layout: NdInterfaceType) -> Self {
+ match layout {
+ NdInterfaceType::Unknown => InterfaceType::Unknown,
+ NdInterfaceType::Ethernet
+ | NdInterfaceType::Ethernet3Megabit
+ | NdInterfaceType::FastEthernetFx
+ | NdInterfaceType::FastEthernetT
+ | NdInterfaceType::GigabitEthernet => InterfaceType::Ethernet,
+ NdInterfaceType::TokenRing => InterfaceType::TokenRing,
+ NdInterfaceType::Fddi => InterfaceType::Fddi,
+ NdInterfaceType::Ppp => InterfaceType::Ppp,
+ NdInterfaceType::Loopback => InterfaceType::Loopback,
+ NdInterfaceType::Slip => InterfaceType::Slip,
+ NdInterfaceType::Atm | NdInterfaceType::IPOverAtm => {
+ InterfaceType::Atm
}
- }
- }
- result
-}
-
-fn ipv4_ser(
- ipv4net: &Vec,
- serializer: S,
-) -> Result {
- let local_ipv4net = ipv4net_to_local(ipv4net);
- local_ipv4net.serialize(serializer)
-}
-
-#[derive(Serialize, Debug, Clone)]
-#[serde(rename_all = "camelCase")]
-struct Ipv6Net {
- pub address: Ipv6Addr,
- pub prefix_length: u8,
- pub netmask: Ipv6Addr,
-}
-
-fn ipv6net_to_local(ipv6net: &Vec) -> Vec {
- let mut result = Vec::new();
- for net in ipv6net {
- match net {
- NdIpv6Net {
- addr,
- prefix_len,
- netmask,
- } => {
- result.push(Ipv6Net {
- address: *addr,
- prefix_length: *prefix_len,
- netmask: *netmask,
- });
+ NdInterfaceType::GenericModem => InterfaceType::GenericModem,
+ NdInterfaceType::Isdn
+ | NdInterfaceType::BasicIsdn
+ | NdInterfaceType::PrimaryIsdn => InterfaceType::Isdn,
+ NdInterfaceType::Wireless80211 => InterfaceType::Wifi,
+ NdInterfaceType::AsymmetricDsl
+ | NdInterfaceType::RateAdaptDsl
+ | NdInterfaceType::SymmetricDsl
+ | NdInterfaceType::VeryHighSpeedDsl
+ | NdInterfaceType::MultiRateSymmetricDsl => InterfaceType::Dsl,
+ NdInterfaceType::Tunnel => InterfaceType::Tunnel,
+ NdInterfaceType::HighPerformanceSerialBus => {
+ InterfaceType::HighPerformanceSerialBus
}
+ NdInterfaceType::Wman
+ | NdInterfaceType::Wwanpp
+ | NdInterfaceType::Wwanpp2 => InterfaceType::MobileBroadband,
+ NdInterfaceType::Bridge => InterfaceType::Bridge,
}
}
- result
-}
-
-fn ipv6_ser(
- ipv6net: &Vec,
- serializer: S,
-) -> Result {
- let local_ipv6net = ipv6net_to_local(ipv6net);
- local_ipv6net.serialize(serializer)
-}
-
-#[derive(Serialize, Debug, Clone)]
-pub struct MacAddr(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8);
-
-fn macaddr_to_local(macaddr: &NdMacAddr) -> Option {
- match macaddr {
- NdMacAddr { .. } => {
- let octets = macaddr.octets();
- Some(MacAddr(
- octets[0], octets[1], octets[2], octets[3], octets[4], octets[5],
- ))
- }
- }
-}
-
-fn macaddr_ser(
- macaddr: &NdMacAddr,
- serializer: S,
-) -> Result {
- let local_macaddr = macaddr_to_local(macaddr);
- local_macaddr.serialize(serializer)
}
diff --git a/packages/desktop/src/providers/network/wifi_hotspot.rs b/packages/desktop/src/providers/network/wifi_hotspot.rs
new file mode 100644
index 00000000..a95d2a22
--- /dev/null
+++ b/packages/desktop/src/providers/network/wifi_hotspot.rs
@@ -0,0 +1,53 @@
+use std::process::Command;
+
+use anyhow::{Context, Result};
+use regex::Regex;
+
+#[derive(Debug)]
+pub struct WifiHotstop {
+ pub ssid: Option,
+ pub signal_strength: Option,
+}
+
+/// Runs the netsh command and parses the output to get the primary
+/// interface's SSID and signal strength.
+//
+/// Unclear if the primary interface is always the default interface
+/// returned by the netdev/defaultnet crate - requires more testing.
+pub fn default_gateway_wifi() -> Result {
+ if cfg!(not(target_os = "windows")) {
+ return Ok(WifiHotstop {
+ ssid: None,
+ signal_strength: None,
+ });
+ }
+
+ let ssid_match = Regex::new(r"(?m)^\s*SSID\s*:\s*(.*?)\r?$").unwrap();
+
+ let signal_match =
+ Regex::new(r"(?m)^\s*Signal\s*:\s*(.*?)\r?$").unwrap();
+
+ let signal_strip = Regex::new(r"(\d+)%").unwrap();
+
+ let output = Command::new("netsh")
+ .args(&["wlan", "show", "interfaces"])
+ .output()
+ .context("Could not run netsh.")?;
+
+ let output = String::from_utf8_lossy(&output.stdout);
+
+ let ssid = ssid_match
+ .captures(&output)
+ .map(|m| m.get(1).unwrap().as_str().to_string());
+ let signal_str: Option<&str> = signal_match
+ .captures(&output)
+ .map(|m| m.get(1).unwrap().as_str());
+ let signal: Option = signal_str
+ .and_then(|s| signal_strip.captures(s))
+ .map(|m| m.get(1).unwrap().as_str().parse().unwrap());
+
+ return Ok(WifiHotstop {
+ ssid,
+ signal_strength: signal,
+ });
+}