From 13f86192264e661f0b067413c9b1f56654a93215 Mon Sep 17 00:00:00 2001 From: stoutes <31317041+stoutes@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:47:28 -0600 Subject: [PATCH 1/6] refactor(#219): add D-Bus operation for context error handling Affected Files: models.rs --- nmrs/src/api/models.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nmrs/src/api/models.rs b/nmrs/src/api/models.rs index 28cb842c..6976a3fc 100644 --- a/nmrs/src/api/models.rs +++ b/nmrs/src/api/models.rs @@ -2132,6 +2132,14 @@ pub enum ConnectionError { /// Bluetooth device not found #[error("Bluetooth device not found")] NoBluetoothDevice, + + /// A D-Bus operation failed with context about what was being attempted + #[error("{context}: {source}")] + DbusOperation { + context: String, + #[source] + source: zbus::Error, + }, } /// NetworkManager device state reason codes. From 85b60fb5605251318e8114fde537ea62ff1e5426 Mon Sep 17 00:00:00 2001 From: stoutes <31317041+stoutes@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:51:44 -0600 Subject: [PATCH 2/6] refactor(#219): add error context to connection_settings.rs Affected Files: connection_settings.rs --- nmrs/src/core/connection_settings.rs | 39 ++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/nmrs/src/core/connection_settings.rs b/nmrs/src/core/connection_settings.rs index 0ea46fcd..497f86eb 100644 --- a/nmrs/src/core/connection_settings.rs +++ b/nmrs/src/core/connection_settings.rs @@ -9,6 +9,7 @@ use std::collections::HashMap; use zbus::Connection; use zvariant::{OwnedObjectPath, Value}; +use crate::api::models::ConnectionError; use crate::util::utils::{connection_settings_proxy, settings_proxy}; use crate::util::validation::validate_ssid; use crate::Result; @@ -39,13 +40,27 @@ pub(crate) async fn get_saved_connection_path( let settings = settings_proxy(conn).await?; - let reply = settings.call_method("ListConnections", &()).await?; + let reply = settings + .call_method("ListConnections", &()) + .await + .map_err(|e| ConnectionError::DbusOperation { + context: "failed to list saved connections".to_string(), + source: e, + })?; + let conns: Vec = reply.body().deserialize()?; for cpath in conns { let cproxy = connection_settings_proxy(conn, cpath.clone()).await?; - let msg = cproxy.call_method("GetSettings", &()).await?; + let msg = cproxy + .call_method("GetSettings", &()) + .await + .map_err(|e| ConnectionError::DbusOperation { + context: format!("failed to get settings for {}", cpath.as_str()), + source: e, + })?; + let body = msg.body(); let all: HashMap> = body.deserialize()?; @@ -75,7 +90,14 @@ pub(crate) async fn has_saved_connection(conn: &Connection, ssid: &str) -> Resul pub(crate) async fn delete_connection(conn: &Connection, conn_path: OwnedObjectPath) -> Result<()> { let cproxy = connection_settings_proxy(conn, conn_path.clone()).await?; - cproxy.call_method("Delete", &()).await?; + cproxy + .call_method("Delete", &()) + .await + .map_err(|e| ConnectionError::DbusOperation { + context: format!("failed to delete connection {}", conn_path.as_str()), + source: e, + })?; + debug!("Deleted connection: {}", conn_path.as_str()); Ok(()) } @@ -87,7 +109,14 @@ pub(crate) async fn delete_connection(conn: &Connection, conn_path: OwnedObjectP pub(crate) async fn list_saved_connections(conn: &Connection) -> Result> { let settings = settings_proxy(conn).await?; - let reply = settings.call_method("ListConnections", &()).await?; + let reply = settings + .call_method("ListConnections", &()) + .await + .map_err(|e| ConnectionError::DbusOperation { + context: "failed to list saved connections".to_string(), + source: e, + })?; + let conns: Vec = reply.body().deserialize()?; let mut connection_names = Vec::new(); @@ -109,4 +138,4 @@ pub(crate) async fn list_saved_connections(conn: &Connection) -> Result Date: Tue, 3 Feb 2026 09:52:15 -0600 Subject: [PATCH 3/6] refactor(#219): add error context to device.rs Affected Files: device.rs --- nmrs/src/core/device.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/nmrs/src/core/device.rs b/nmrs/src/core/device.rs index 186ba179..04fbfb7c 100644 --- a/nmrs/src/core/device.rs +++ b/nmrs/src/core/device.rs @@ -21,7 +21,13 @@ use crate::Result; /// type (Ethernet, Wi-Fi, etc.), current state, and driver. pub(crate) async fn list_devices(conn: &Connection) -> Result> { let proxy = NMProxy::new(conn).await?; - let paths = proxy.get_devices().await?; + let paths = proxy + .get_devices() + .await + .map_err(|e| ConnectionError::DbusOperation { + context: "failed to get device paths from NetworkManager".to_string(), + source: e, + })?; let mut devices = Vec::new(); for p in paths { @@ -30,8 +36,17 @@ pub(crate) async fn list_devices(conn: &Connection) -> Result> { .build() .await?; - let interface = d_proxy.interface().await?; - let raw_type = d_proxy.device_type().await?; + let interface = d_proxy.interface().await + .map_err(|e| ConnectionError::DbusOperation { + context: format!("failed to get interface name for device {}", p.as_str()), + source: e, + })?; + + let raw_type = d_proxy.device_type().await + .map_err(|e| ConnectionError::DbusOperation { + context: format!("failed to get device type for {}", interface), + source: e, + })?; let current_mac = match d_proxy.hw_address().await { Ok(addr) => addr, Err(e) => { @@ -128,9 +143,18 @@ pub(crate) async fn list_bluetooth_devices(conn: &Connection) -> Result Date: Tue, 3 Feb 2026 09:52:46 -0600 Subject: [PATCH 4/6] refactor(#219): add error context to scan.rs Affected Files: scan.rs --- nmrs/src/core/scan.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/nmrs/src/core/scan.rs b/nmrs/src/core/scan.rs index 0de2af4b..62498479 100644 --- a/nmrs/src/core/scan.rs +++ b/nmrs/src/core/scan.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; use zbus::Connection; -use crate::api::models::Network; +use crate::api::models::{ConnectionError, Network}; use crate::dbus::{NMAccessPointProxy, NMDeviceProxy, NMProxy, NMWirelessProxy}; use crate::monitoring::info::current_ssid; use crate::types::constants::{device_type, security_flags}; @@ -30,7 +30,13 @@ pub(crate) async fn scan_networks(conn: &Connection) -> Result<()> { .build() .await?; - if d_proxy.device_type().await? != device_type::WIFI { + let dev_type = d_proxy.device_type().await + .map_err(|e| ConnectionError::DbusOperation { + context: format!("failed to get device type for {} during Wi-Fi scan", dp.as_str()), + source: e, + })?; + + if dev_type != device_type::WIFI { continue; } @@ -40,7 +46,11 @@ pub(crate) async fn scan_networks(conn: &Connection) -> Result<()> { .await?; let opts = std::collections::HashMap::new(); - wifi.request_scan(opts).await?; + wifi.request_scan(opts).await + .map_err(|e| ConnectionError::DbusOperation { + context: format!("failed to request Wi-Fi scan on device {}", dp.as_str()), + source: e, + })?; } Ok(()) From 0d07b15dbe551384007941109a381bfc810e814d Mon Sep 17 00:00:00 2001 From: stoutes <31317041+stoutes@users.noreply.github.com> Date: Tue, 3 Feb 2026 09:53:02 -0600 Subject: [PATCH 5/6] refactor(#219): add error context to vpn.rs Affected Files: vpn.rs --- nmrs/src/core/vpn.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/nmrs/src/core/vpn.rs b/nmrs/src/core/vpn.rs index a14661af..bd8fd0f7 100644 --- a/nmrs/src/core/vpn.rs +++ b/nmrs/src/core/vpn.rs @@ -16,7 +16,7 @@ use zbus::Connection; use zvariant::OwnedObjectPath; use crate::api::models::{ - ConnectionOptions, DeviceState, TimeoutConfig, VpnConnection, VpnConnectionInfo, + ConnectionOptions, ConnectionError, DeviceState, TimeoutConfig, VpnConnection, VpnConnectionInfo, VpnCredentials, VpnType, }; use crate::builders::build_wireguard_connection; @@ -273,7 +273,12 @@ pub(crate) async fn list_vpn_connections(conn: &Connection) -> Result = list_reply.body().deserialize()?; // Map active WireGuard connection id -> (state, interface) @@ -563,7 +568,11 @@ pub(crate) async fn forget_vpn(conn: &Connection, name: &str) -> Result<()> { if id_ok && type_ok { debug!("Found WireGuard connection, deleting: {name}"); - cproxy.call_method("Delete", &()).await?; + cproxy.call_method("Delete", &()).await + .map_err(|e| ConnectionError::DbusOperation { + context: format!("failed to delete VPN connection '{}'", name), + source: e, + })?; info!("Successfully deleted VPN connection: {name}"); return Ok(()); } From ed478637f94c00331c945bfd8b385e2e687c8110 Mon Sep 17 00:00:00 2001 From: stoutes <31317041+stoutes@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:27:21 -0600 Subject: [PATCH 6/6] refactor(#219): cargo fmt Affected Files: connection_settings.rs, device.rs, scan.rs, vpn.rs --- nmrs/src/core/connection_settings.rs | 11 +++++------ nmrs/src/core/device.rs | 13 ++++++++++--- nmrs/src/core/scan.rs | 12 +++++++++--- nmrs/src/core/vpn.rs | 15 +++++++++------ 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/nmrs/src/core/connection_settings.rs b/nmrs/src/core/connection_settings.rs index 497f86eb..11425431 100644 --- a/nmrs/src/core/connection_settings.rs +++ b/nmrs/src/core/connection_settings.rs @@ -53,13 +53,12 @@ pub(crate) async fn get_saved_connection_path( for cpath in conns { let cproxy = connection_settings_proxy(conn, cpath.clone()).await?; - let msg = cproxy - .call_method("GetSettings", &()) - .await - .map_err(|e| ConnectionError::DbusOperation { + let msg = cproxy.call_method("GetSettings", &()).await.map_err(|e| { + ConnectionError::DbusOperation { context: format!("failed to get settings for {}", cpath.as_str()), source: e, - })?; + } + })?; let body = msg.body(); let all: HashMap> = body.deserialize()?; @@ -138,4 +137,4 @@ pub(crate) async fn list_saved_connections(conn: &Connection) -> Result Result> { .build() .await?; - let interface = d_proxy.interface().await + let interface = d_proxy + .interface() + .await .map_err(|e| ConnectionError::DbusOperation { context: format!("failed to get interface name for device {}", p.as_str()), source: e, })?; - let raw_type = d_proxy.device_type().await + let raw_type = d_proxy + .device_type() + .await .map_err(|e| ConnectionError::DbusOperation { context: format!("failed to get device type for {}", interface), source: e, @@ -147,7 +151,10 @@ pub(crate) async fn list_bluetooth_devices(conn: &Connection) -> Result Result<()> { .build() .await?; - let dev_type = d_proxy.device_type().await + let dev_type = d_proxy + .device_type() + .await .map_err(|e| ConnectionError::DbusOperation { - context: format!("failed to get device type for {} during Wi-Fi scan", dp.as_str()), + context: format!( + "failed to get device type for {} during Wi-Fi scan", + dp.as_str() + ), source: e, })?; @@ -46,7 +51,8 @@ pub(crate) async fn scan_networks(conn: &Connection) -> Result<()> { .await?; let opts = std::collections::HashMap::new(); - wifi.request_scan(opts).await + wifi.request_scan(opts) + .await .map_err(|e| ConnectionError::DbusOperation { context: format!("failed to request Wi-Fi scan on device {}", dp.as_str()), source: e, diff --git a/nmrs/src/core/vpn.rs b/nmrs/src/core/vpn.rs index bd8fd0f7..5cd77d4d 100644 --- a/nmrs/src/core/vpn.rs +++ b/nmrs/src/core/vpn.rs @@ -16,8 +16,8 @@ use zbus::Connection; use zvariant::OwnedObjectPath; use crate::api::models::{ - ConnectionOptions, ConnectionError, DeviceState, TimeoutConfig, VpnConnection, VpnConnectionInfo, - VpnCredentials, VpnType, + ConnectionError, ConnectionOptions, DeviceState, TimeoutConfig, VpnConnection, + VpnConnectionInfo, VpnCredentials, VpnType, }; use crate::builders::build_wireguard_connection; use crate::core::state_wait::wait_for_connection_activation; @@ -273,7 +273,9 @@ pub(crate) async fn list_vpn_connections(conn: &Connection) -> Result Result<()> { if id_ok && type_ok { debug!("Found WireGuard connection, deleting: {name}"); - cproxy.call_method("Delete", &()).await - .map_err(|e| ConnectionError::DbusOperation { + cproxy.call_method("Delete", &()).await.map_err(|e| { + ConnectionError::DbusOperation { context: format!("failed to delete VPN connection '{}'", name), source: e, - })?; + } + })?; info!("Successfully deleted VPN connection: {name}"); return Ok(()); }