dg_network_manager 1.0.0

Network Manager DBUS API
Documentation
pub mod connection;

use crate::dbus_api::Options;
use crate::dbus_api::settings::connection::ConnectionSettingsClient;
use crate::utils::parse_dbus_variant;
use std::collections::HashMap;
use std::io::{Error, ErrorKind};
use std::sync::Arc;
use zbus::proxy::CacheProperties;
use zbus::zvariant::{OwnedObjectPath, OwnedValue, Value};
use zbus::{Connection, Result as ZResult, proxy};

pub const NM_SETTINGS_PATH: &str = "/org/freedesktop/NetworkManager/Settings";

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

    fn get_connection_by_uuid(&self, uuid: &str) -> ZResult<OwnedObjectPath>;

    fn add_connection(
        &self,
        connection: HashMap<&str, HashMap<&str, Value<'_>>>,
    ) -> ZResult<OwnedObjectPath>;

    fn add_connection_unsaved(
        &self,
        connection: HashMap<&str, HashMap<&str, Value<'_>>>,
    ) -> ZResult<OwnedObjectPath>;

    fn add_connection2(
        &self,
        settings: HashMap<&str, HashMap<&str, Value<'_>>>,
        flags: u32,
        args: HashMap<&str, Value<'_>>,
    ) -> ZResult<(OwnedObjectPath, HashMap<String, OwnedValue>)>;

    fn load_connections(&self, filenames: Vec<&str>) -> ZResult<(bool, Vec<String>)>;

    fn reload_connections(&self) -> ZResult<bool>;

    fn save_hostname(&self, hostname: &str) -> ZResult<()>;

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

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

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

pub struct SettingsClient {
    service_path: String,
    proxy: SettingsProxy<'static>,
    connection: Arc<Connection>,
}
impl SettingsClient {
    pub async fn new(connection: Arc<Connection>, service_path: String) -> Result<Self, Error> {
        let proxy = SettingsProxy::builder(&connection)
            .path(service_path.clone())
            .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()))?;

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

    pub fn service_path(&self) -> &str {
        &self.service_path
    }

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

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

        Ok(connections)
    }

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

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

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

    pub async fn add_connection2(
        &self,
        settings: HashMap<&str, HashMap<&str, Value<'_>>>,
        flags: u32,
        args: HashMap<&str, Value<'_>>,
    ) -> Result<(OwnedObjectPath, Options), Error> {
        self.proxy
            .add_connection2(settings, flags, args)
            .await
            .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))
    }

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

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

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

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

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

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

    pub async fn find_connection_by_id(
        &self,
        id: &str,
    ) -> Result<Option<ConnectionSettingsClient>, Error> {
        let connections = self.list_connections().await?;
        for conn in connections {
            let settings = conn.get_settings().await?;
            if let Some(connection_settings) = settings.get("connection") {
                if let Some(conn_id) = connection_settings.get("id").cloned() {
                    if id == parse_dbus_variant(conn_id.into()) {
                        return Ok(Some(conn));
                    }
                }
            }
        }
        Ok(None)
    }
}