wifi-manager 0.1.2

A cross-platform Wi-Fi management library for Rust, supporting Linux and Windows.
Documentation
use std::{collections::HashMap, fmt::Display, ops::Deref, path::Path};

use tokio::process::Command;
use windows::{Devices::WiFi::WiFiAdapter, core::*};

use crate::{BandWidth, BandWidthArg, Impl, Interface, Mode, WiFiError, WiFiResult};

#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ID(GUID);

impl Display for ID {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self.0)
    }
}

impl Deref for ID {
    type Target = GUID;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<GUID> for ID {
    fn from(guid: GUID) -> Self {
        Self(guid)
    }
}

pub struct OS;

impl Impl for OS {
    async fn check_environment() -> WiFiResult {
        let root = std::env::var("SYSTEMROOT").unwrap();
        let path = Path::new(&root).join("System32").join("Npcap");
        if !path.exists() {
            return Err(WiFiError::new_system(
                "Npcap not installed, see https://crates.io/crates/pcap",
            ));
        }
        Ok(())
    }

    async fn interface_list() -> std::result::Result<Vec<Interface>, crate::WiFiError> {
        let id_list = interface_id_list().await?;
        let mut out = Vec::new();
        for id in id_list.into_iter() {
            let support_mode = interface_support_modes(&id).await?;

            out.push(Interface { id, support_mode });
        }

        Ok(out)
    }

    async fn set_mode(id: &ID, mode: Mode) -> WiFiResult {
        run_wlan_helper_command(id, &["mode", mode.cmd()]).await
    }

    async fn set_channel(id: &ID, channel: usize, _band_width: Option<BandWidthArg>) -> WiFiResult {
        run_wlan_helper_command(id, &["channel", &channel.to_string()]).await
    }

    async fn ifup(_id: &ID) -> WiFiResult {
        Ok(())
    }

    async fn ifdown(_id: &ID) -> WiFiResult {
        Ok(())
    }

    async fn freq_max_bandwidth(
        _id: &ID,
    ) -> WiFiResult<std::collections::HashMap<usize, BandWidth>> {
        Ok(HashMap::new())
    }
}

impl From<windows::core::Error> for WiFiError {
    fn from(value: windows::core::Error) -> Self {
        WiFiError::System(value.to_string())
    }
}

fn wlan_helper() -> WiFiResult<Command> {
    let root = std::env::var("SYSTEMROOT").unwrap();
    let path = Path::new(&root).join("System32").join("Npcap");
    if !path.exists() {
        Err(WiFiError::new_system(
            "Npcap not installed, see https://crates.io/crates/pcap",
        ))?;
    }
    let mut cmd = Command::new("WlanHelper.exe");
    cmd.env("PATH", format!("{};%PATH%", path.display()));
    Ok(cmd)
}

// Helper function to run wlan_helper commands with error handling
async fn run_wlan_helper_command(id: &ID, args: &[&str]) -> WiFiResult {
    let mut cmd = wlan_helper()?;
    cmd.arg(id.to_string());
    for arg in args {
        cmd.arg(arg);
    }
    let output = cmd.output().await?;
    if !output.status.success() {
        let out = String::from_utf8_lossy(&output.stdout);
        return Err(WiFiError::new_system(out.to_string()));
    }
    Ok(())
}

async fn interface_support_modes(id: &ID) -> WiFiResult<Vec<Mode>> {
    let mut cmd = wlan_helper()?;
    let out = cmd.arg(format!("{id:?}")).arg("modes").output().await?;
    let stdout = String::from_utf8_lossy(&out.stdout);
    let modes = stdout
        .trim()
        .split(',')
        .filter_map(|o| o.try_into().ok())
        .collect();

    Ok(modes)
}

async fn interface_id_list() -> Result<Vec<ID>> {
    let adapters = WiFiAdapter::FindAllAdaptersAsync()?.get()?;
    let mut adapter_names = Vec::new();

    for adapter in adapters {
        if let Ok(adapter) = adapter.NetworkAdapter() {
            let id = adapter.NetworkAdapterId()?;
            adapter_names.push(id.into());
        }
    }

    Ok(adapter_names)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_get_wifi_adapter_names() {
        match OS::interface_list().await {
            Ok(adapters) => {
                println!("找到以下WiFi适配器:");
                for (i, name) in adapters.iter().enumerate() {
                    println!("{}. {:?}", i + 1, name);
                }
            }
            Err(e) => println!("获取WiFi适配器失败: {:?}", e),
        }
    }

    #[tokio::test]
    async fn test_wlan_helper() {
        let mut wlan_helper = wlan_helper().unwrap();
        let out = wlan_helper.arg("-h").output().await.unwrap();
        println!("{}", String::from_utf8_lossy(&out.stdout));
    }

    #[tokio::test]
    async fn test_wlan_helper_mode() {}
}