dg_network_manager 1.0.0

Network Manager DBUS API
Documentation
use super::DeviceClient;
use crate::dbus_api::access_point::AccessPoint;
use crate::dbus_api::active_connection::ActiveConnectionClient;
use crate::dbus_api::devices::statistics::DeviceStatistics;
use std::collections::HashMap;
use std::io::{Error, ErrorKind};
use std::sync::Arc;
use zbus::proxy::CacheProperties;
use zbus::zvariant::{OwnedObjectPath, Value};
use zbus::{Connection, Result as ZResult, proxy};

#[proxy(
    interface = "org.freedesktop.NetworkManager.Device.Wireless",
    default_service = "org.freedesktop.NetworkManager"
)]
pub trait Wireless {
    // Methods
    fn get_access_points(&self) -> ZResult<Vec<OwnedObjectPath>>;

    fn get_all_access_points(&self) -> ZResult<Vec<OwnedObjectPath>>;

    fn request_scan(&self, options: HashMap<&str, Value<'_>>) -> ZResult<()>;

    // Properties
    #[zbus(property)]
    fn hw_address(&self) -> ZResult<String>;

    #[zbus(property)]
    fn perm_hw_address(&self) -> ZResult<String>;

    #[zbus(property)]
    fn mode(&self) -> ZResult<u32>;

    #[zbus(property)]
    fn bitrate(&self) -> ZResult<u32>;

    #[zbus(property)]
    fn access_points(&self) -> ZResult<Vec<OwnedObjectPath>>;

    #[zbus(property)]
    fn active_access_point(&self) -> ZResult<OwnedObjectPath>;

    #[zbus(property)]
    fn wireless_capabilities(&self) -> ZResult<u32>;

    #[zbus(property)]
    fn last_scan(&self) -> ZResult<i64>;
}

#[derive(Debug, Clone)]
pub struct WirelessClient {
    device: DeviceClient,
    proxy: WirelessProxy<'static>,
    connection: Arc<Connection>,
}

impl WirelessClient {
    pub async fn new(connection: Arc<Connection>, service_path: String) -> Result<Self, Error> {
        let device = DeviceClient::new(connection.clone(), service_path.clone()).await?;

        let proxy = WirelessProxy::builder(&connection)
            .path(service_path)
            .map_err(|e| Error::new(ErrorKind::InvalidInput, e.to_string()))?
            .cache_properties(CacheProperties::Yes)
            .build()
            .await
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;

        // Verify we can access wireless-specific properties
        proxy
            .hw_address()
            .await
            .map_err(|e| Error::new(ErrorKind::NotFound, e.to_string()))?;

        Ok(Self {
            device,
            proxy,
            connection,
        })
    }

    // Forward common device methods to the base device client
    pub fn service_path(&self) -> &str {
        self.device.service_path()
    }

    pub async fn interface(&self) -> Result<String, Error> {
        self.device.interface().await
    }

    pub async fn disconnect(&self) -> Result<(), Error> {
        self.device.disconnect().await
    }

    pub async fn active_connection(&self) -> Result<Option<ActiveConnectionClient>, Error> {
        self.device.active_connection().await
    }

    pub async fn ip4_config(&self) -> Result<OwnedObjectPath, Error> {
        self.device.ip4_config().await
    }

    pub async fn get_statistics(&self) -> Result<DeviceStatistics, Error> {
        self.device.get_statistics().await
    }

    pub async fn set_statistics_refresh_rate(&self, rate_ms: u32) -> Result<(), Error> {
        self.device.set_statistics_refresh_rate(rate_ms).await
    }

    pub async fn get_tx_bytes(&self) -> Result<u64, Error> {
        self.device.get_tx_bytes().await
    }

    pub async fn get_rx_bytes(&self) -> Result<u64, Error> {
        self.device.get_rx_bytes().await
    }

    // Wireless-specific methods
    pub async fn get_access_points(&self) -> Result<Vec<AccessPoint>, Error> {
        let paths = self
            .proxy
            .get_access_points()
            .await
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;

        let mut access_points = Vec::new();
        for path in paths {
            match AccessPoint::new(self.connection.clone(), path.to_string()).await {
                Ok(ap) => access_points.push(ap),
                Err(_) => continue,
            }
        }

        Ok(access_points)
    }

    pub async fn get_all_access_points(
        &self,
        connection: Arc<Connection>,
    ) -> Result<Vec<AccessPoint>, Error> {
        let paths = self
            .proxy
            .get_all_access_points()
            .await
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;

        let mut access_points = Vec::new();
        for path in paths {
            match AccessPoint::new(connection.clone(), path.to_string()).await {
                Ok(ap) => access_points.push(ap),
                Err(_) => continue,
            }
        }

        Ok(access_points)
    }

    pub async fn request_scan(&self, options: HashMap<&str, Value<'_>>) -> Result<(), Error> {
        self.proxy
            .request_scan(options)
            .await
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
    }

    pub async fn hw_address(&self) -> Result<String, Error> {
        self.proxy
            .hw_address()
            .await
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
    }

    pub async fn perm_hw_address(&self) -> Result<String, Error> {
        self.proxy
            .perm_hw_address()
            .await
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
    }

    pub async fn mode(&self) -> Result<u32, Error> {
        self.proxy
            .mode()
            .await
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
    }

    pub async fn bitrate(&self) -> Result<u32, Error> {
        self.proxy
            .bitrate()
            .await
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
    }

    pub async fn active_access_point(&self) -> Result<OwnedObjectPath, Error> {
        self.proxy
            .active_access_point()
            .await
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
    }

    pub async fn get_active_access_point(&self) -> Result<Option<AccessPoint>, Error> {
        match self.proxy.active_access_point().await {
            Ok(path) => match AccessPoint::new(self.connection.clone(), path.to_string()).await {
                Ok(ap) => Ok(Some(ap)),
                Err(_) => Ok(None),
            },
            Err(_) => Ok(None),
        }
    }

    pub async fn wireless_capabilities(&self) -> Result<u32, Error> {
        self.proxy
            .wireless_capabilities()
            .await
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
    }

    pub async fn last_scan(&self) -> Result<i64, Error> {
        self.proxy
            .last_scan()
            .await
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
    }

    // Convenience method for empty scan options
    pub async fn simple_scan(&self) -> Result<(), Error> {
        let options = HashMap::new();
        self.request_scan(options).await
    }
}