From 3bc74473126012335faee31c78da72be26ec69be Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sat, 28 Feb 2026 11:13:01 -0500 Subject: [PATCH 1/2] refactor(nmrs): `#[must_use]` added across public interface --- nmrs/CHANGELOG.md | 14 +++-- nmrs/Cargo.toml | 2 +- nmrs/src/api/builders/bluetooth.rs | 2 + nmrs/src/api/builders/connection_builder.rs | 29 +++++++++ nmrs/src/api/builders/wifi.rs | 2 + nmrs/src/api/builders/wifi_builder.rs | 17 +++++ nmrs/src/api/builders/wireguard_builder.rs | 12 ++++ nmrs/src/api/models.rs | 69 ++++++++++++++++++--- nmrs/src/api/network_manager.rs | 1 + 9 files changed, 135 insertions(+), 13 deletions(-) diff --git a/nmrs/CHANGELOG.md b/nmrs/CHANGELOG.md index 556ffd54..93021fe0 100644 --- a/nmrs/CHANGELOG.md +++ b/nmrs/CHANGELOG.md @@ -4,14 +4,18 @@ All notable changes to the `nmrs` crate will be documented in this file. ## [Unreleased] +## [2.1.0] - 2026-02-28 +### Added +- `#[must_use]` attributes across public API: constructors, builder methods, and pure functions ([#220](https://github.com/cachebag/nmrs/issues/220)) + ## [2.0.1] - 2026-02-25 ### Changed -- Completed IPv6 support ([#208](https://github.com/cachebag/nmrs/issues/208)) +- Completed IPv6 support ([#208](https://github.com/cachebag/nmrs/pull/208)) - Replace magic number with named constant for device states ([#230](https://github.com/cachebag/nmrs/pull/230)) -- Replaced hardcoded root paths with `Default` impl ([#224](https://github.com/cachebag/nmrs/issues/224)) -- Add context to D-Bus operation errors ([#240](https://github.com/cachebag/nmrs/issues/240)) -- Replace `println!` with `debug!` ([#234](https://github.com/cachebag/nmrs/issues/234)) -- Idempotence enforcement for `forget_vpn()` ([#232](https://github.com/cachebag/nmrs/issues/232)) +- Replaced hardcoded root paths with `Default` impl ([#224](https://github.com/cachebag/nmrs/pull/224)) +- Add context to D-Bus operation errors ([#240](https://github.com/cachebag/nmrs/pull/240)) +- Replace `println!` with `debug!` ([#234](https://github.com/cachebag/nmrs/pull/234)) +- Idempotence enforcement for `forget_vpn()` ([#232](https://github.com/cachebag/nmrs/pull/232)) ### Added - validate bluetooth address in `populate_bluez_info` & `BluetoothIdentity::new` ([#215](https://github.com/cachebag/nmrs/issues/215)) diff --git a/nmrs/Cargo.toml b/nmrs/Cargo.toml index 2be4ed73..4cea88a9 100644 --- a/nmrs/Cargo.toml +++ b/nmrs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nmrs" -version = "2.0.1" +version = "2.1.0" authors = ["Akrm Al-Hakimi "] edition.workspace = true rust-version = "1.85.1" diff --git a/nmrs/src/api/builders/bluetooth.rs b/nmrs/src/api/builders/bluetooth.rs index d04ca3c8..91d35798 100644 --- a/nmrs/src/api/builders/bluetooth.rs +++ b/nmrs/src/api/builders/bluetooth.rs @@ -31,6 +31,7 @@ use crate::{ }; /// Builds the `connection` section with type, id, uuid, and autoconnect settings. +#[must_use] pub fn base_connection_section( name: &str, opts: &ConnectionOptions, @@ -64,6 +65,7 @@ fn bluetooth_section(settings: &BluetoothIdentity) -> HashMap<&'static str, Valu s } +#[must_use] pub fn build_bluetooth_connection( name: &str, settings: &BluetoothIdentity, diff --git a/nmrs/src/api/builders/connection_builder.rs b/nmrs/src/api/builders/connection_builder.rs index 5a660844..7bf72203 100644 --- a/nmrs/src/api/builders/connection_builder.rs +++ b/nmrs/src/api/builders/connection_builder.rs @@ -41,6 +41,7 @@ pub struct IpConfig { impl IpConfig { /// Creates a new IP configuration. + #[must_use] pub fn new(address: impl Into, prefix: u32) -> Self { Self { address: address.into(), @@ -60,6 +61,7 @@ pub struct Route { impl Route { /// Creates a new route configuration. + #[must_use] pub fn new(dest: impl Into, prefix: u32) -> Self { Self { dest: dest.into(), @@ -70,12 +72,14 @@ impl Route { } /// Sets the next hop gateway for this route. + #[must_use] pub fn next_hop(mut self, gateway: impl Into) -> Self { self.next_hop = Some(gateway.into()); self } /// Sets the metric (priority) for this route. + #[must_use] pub fn metric(mut self, metric: u32) -> Self { self.metric = Some(metric); self @@ -130,6 +134,7 @@ impl ConnectionBuilder { /// /// let builder = ConnectionBuilder::new("802-11-wireless", "HomeNetwork"); /// ``` + #[must_use] pub fn new(connection_type: &str, id: impl Into) -> Self { let mut settings = HashMap::new(); let mut connection = HashMap::new(); @@ -147,6 +152,7 @@ impl ConnectionBuilder { /// /// By default, a random UUID is generated. Use this to specify a deterministic /// UUID for testing or when recreating existing connections. + #[must_use] pub fn uuid(mut self, uuid: Uuid) -> Self { if let Some(conn) = self.settings.get_mut("connection") { conn.insert("uuid", Value::from(uuid.to_string())); @@ -157,6 +163,7 @@ impl ConnectionBuilder { /// Sets the network interface name for this connection. /// /// This restricts the connection to a specific interface (e.g., "wlan0", "eth0"). + #[must_use] pub fn interface_name(mut self, name: impl Into) -> Self { if let Some(conn) = self.settings.get_mut("connection") { conn.insert("interface-name", Value::from(name.into())); @@ -165,6 +172,7 @@ impl ConnectionBuilder { } /// Enables or disables automatic connection on boot/availability. + #[must_use] pub fn autoconnect(mut self, enabled: bool) -> Self { if let Some(conn) = self.settings.get_mut("connection") { conn.insert("autoconnect", Value::from(enabled)); @@ -176,6 +184,7 @@ impl ConnectionBuilder { /// /// When multiple connections are available, NetworkManager connects to the /// one with the highest priority. Default is 0. + #[must_use] pub fn autoconnect_priority(mut self, priority: i32) -> Self { if let Some(conn) = self.settings.get_mut("connection") { conn.insert("autoconnect-priority", Value::from(priority)); @@ -187,6 +196,7 @@ impl ConnectionBuilder { /// /// After this many failed attempts, the connection won't auto-retry. /// Default is -1 (unlimited retries). + #[must_use] pub fn autoconnect_retries(mut self, retries: i32) -> Self { if let Some(conn) = self.settings.get_mut("connection") { conn.insert("autoconnect-retries", Value::from(retries)); @@ -197,6 +207,7 @@ impl ConnectionBuilder { /// Applies multiple connection options at once. /// /// This is a convenience method to apply all fields from `ConnectionOptions`. + #[must_use] pub fn options(mut self, opts: &ConnectionOptions) -> Self { if let Some(conn) = self.settings.get_mut("connection") { conn.insert("autoconnect", Value::from(opts.autoconnect)); @@ -213,6 +224,7 @@ impl ConnectionBuilder { } /// Configures IPv4 to use automatic configuration (DHCP). + #[must_use] pub fn ipv4_auto(mut self) -> Self { let mut ipv4 = HashMap::new(); ipv4.insert("method", Value::from("auto")); @@ -233,6 +245,7 @@ impl ConnectionBuilder { /// ]) /// .build(); /// ``` + #[must_use] pub fn ipv4_manual(mut self, addresses: Vec) -> Self { let mut ipv4 = HashMap::new(); ipv4.insert("method", Value::from("manual")); @@ -254,6 +267,7 @@ impl ConnectionBuilder { } /// Disables IPv4 for this connection. + #[must_use] pub fn ipv4_disabled(mut self) -> Self { let mut ipv4 = HashMap::new(); ipv4.insert("method", Value::from("disabled")); @@ -262,6 +276,7 @@ impl ConnectionBuilder { } /// Configures IPv4 to use link-local addressing (169.254.x.x). + #[must_use] pub fn ipv4_link_local(mut self) -> Self { let mut ipv4 = HashMap::new(); ipv4.insert("method", Value::from("link-local")); @@ -272,6 +287,7 @@ impl ConnectionBuilder { /// Configures IPv4 for internet connection sharing. /// /// The connection will provide DHCP and NAT for other devices. + #[must_use] pub fn ipv4_shared(mut self) -> Self { let mut ipv4 = HashMap::new(); ipv4.insert("method", Value::from("shared")); @@ -282,6 +298,7 @@ impl ConnectionBuilder { /// Sets IPv4 DNS servers. /// /// DNS servers are specified as integers (network byte order). + #[must_use] pub fn ipv4_dns(mut self, servers: Vec) -> Self { let dns_u32: Vec = servers.into_iter().map(u32::from).collect(); @@ -292,6 +309,7 @@ impl ConnectionBuilder { } /// Sets the IPv4 gateway. + #[must_use] pub fn ipv4_gateway(mut self, gateway: Ipv4Addr) -> Self { if let Some(ipv4) = self.settings.get_mut("ipv4") { ipv4.insert("gateway", Value::from(gateway.to_string())); @@ -300,6 +318,7 @@ impl ConnectionBuilder { } /// Adds IPv4 static routes. + #[must_use] pub fn ipv4_routes(mut self, routes: Vec) -> Self { let route_data: Vec>> = routes .into_iter() @@ -327,6 +346,7 @@ impl ConnectionBuilder { } /// Configures IPv6 to use automatic configuration (SLAAC/DHCPv6). + #[must_use] pub fn ipv6_auto(mut self) -> Self { let mut ipv6 = HashMap::new(); ipv6.insert("method", Value::from("auto")); @@ -335,6 +355,7 @@ impl ConnectionBuilder { } /// Configures IPv6 with manual (static) addresses. + #[must_use] pub fn ipv6_manual(mut self, addresses: Vec) -> Self { let mut ipv6 = HashMap::new(); ipv6.insert("method", Value::from("manual")); @@ -355,6 +376,7 @@ impl ConnectionBuilder { } /// Disables IPv6 for this connection. + #[must_use] pub fn ipv6_ignore(mut self) -> Self { let mut ipv6 = HashMap::new(); ipv6.insert("method", Value::from("ignore")); @@ -363,6 +385,7 @@ impl ConnectionBuilder { } /// Configures IPv6 to use link-local addressing only. + #[must_use] pub fn ipv6_link_local(mut self) -> Self { let mut ipv6 = HashMap::new(); ipv6.insert("method", Value::from("link-local")); @@ -371,6 +394,7 @@ impl ConnectionBuilder { } /// Sets IPv6 DNS servers. + #[must_use] pub fn ipv6_dns(mut self, servers: Vec) -> Self { let dns_strings: Vec = servers.into_iter().map(|s| s.to_string()).collect(); @@ -381,6 +405,7 @@ impl ConnectionBuilder { } /// Sets the IPv6 gateway. + #[must_use] pub fn ipv6_gateway(mut self, gateway: Ipv6Addr) -> Self { if let Some(ipv6) = self.settings.get_mut("ipv6") { ipv6.insert("gateway", Value::from(gateway.to_string())); @@ -389,6 +414,7 @@ impl ConnectionBuilder { } /// Adds IPv6 static routes. + #[must_use] pub fn ipv6_routes(mut self, routes: Vec) -> Self { let route_data: Vec>> = routes .into_iter() @@ -435,6 +461,7 @@ impl ConnectionBuilder { /// .with_section("bridge", bridge_section) /// .build(); /// ``` + #[must_use] pub fn with_section( mut self, name: &'static str, @@ -462,6 +489,7 @@ impl ConnectionBuilder { /// }) /// .build(); /// ``` + #[must_use] pub fn update_section(mut self, name: &'static str, f: F) -> Self where F: FnOnce(&mut HashMap<&'static str, Value<'static>>), @@ -476,6 +504,7 @@ impl ConnectionBuilder { /// /// This consumes the builder and returns the complete settings structure /// ready to be passed to NetworkManager's D-Bus API. + #[must_use] pub fn build(self) -> HashMap<&'static str, HashMap<&'static str, Value<'static>>> { self.settings } diff --git a/nmrs/src/api/builders/wifi.rs b/nmrs/src/api/builders/wifi.rs index 9e664cad..1b44a2ff 100644 --- a/nmrs/src/api/builders/wifi.rs +++ b/nmrs/src/api/builders/wifi.rs @@ -57,6 +57,7 @@ use crate::api::models::{self, ConnectionOptions}; /// /// This function is maintained for backward compatibility. For new code, /// consider using `WifiConnectionBuilder` for a more ergonomic API. +#[must_use] pub fn build_wifi_connection( ssid: &str, security: &models::WifiSecurity, @@ -91,6 +92,7 @@ pub fn build_wifi_connection( /// /// This function is maintained for backward compatibility. For new code, /// consider using `EthernetConnectionBuilder` for a more ergonomic API. +#[must_use] pub fn build_ethernet_connection( connection_id: &str, opts: &ConnectionOptions, diff --git a/nmrs/src/api/builders/wifi_builder.rs b/nmrs/src/api/builders/wifi_builder.rs index 4bb9d2f5..244ad407 100644 --- a/nmrs/src/api/builders/wifi_builder.rs +++ b/nmrs/src/api/builders/wifi_builder.rs @@ -122,6 +122,7 @@ impl WifiConnectionBuilder { /// /// By default, the connection is configured as an open network. Use /// `.wpa_psk()` or `.wpa_eap()` to add security. + #[must_use] pub fn new(ssid: impl Into) -> Self { let ssid = ssid.into(); let inner = ConnectionBuilder::new("802-11-wireless", &ssid); @@ -140,6 +141,7 @@ impl WifiConnectionBuilder { /// Configures this as an open (unsecured) network. /// /// This is the default, but can be called explicitly for clarity. + #[must_use] pub fn open(self) -> Self { // Open networks don't need a security section Self { @@ -151,6 +153,7 @@ impl WifiConnectionBuilder { /// Configures WPA-PSK (Personal) security with the given passphrase. /// /// Uses WPA2 (RSN) with CCMP encryption. + #[must_use] pub fn wpa_psk(mut self, psk: impl Into) -> Self { let mut security = HashMap::new(); security.insert("key-mgmt", Value::from("wpa-psk")); @@ -173,6 +176,7 @@ impl WifiConnectionBuilder { /// Configures WPA-EAP (Enterprise) security with 802.1X authentication. /// /// Supports PEAP and TTLS methods with various inner authentication protocols. + #[must_use] pub fn wpa_eap(mut self, opts: models::EapOptions) -> Self { let mut security = HashMap::new(); security.insert("key-mgmt", Value::from("wpa-eap")); @@ -219,12 +223,14 @@ impl WifiConnectionBuilder { } /// Marks this network as hidden (doesn't broadcast SSID). + #[must_use] pub fn hidden(mut self, hidden: bool) -> Self { self.hidden = Some(hidden); self } /// Restricts connection to a specific WiFi band. + #[must_use] pub fn band(mut self, band: WifiBand) -> Self { self.band = Some(band); self @@ -233,6 +239,7 @@ impl WifiConnectionBuilder { /// Restricts connection to a specific access point by BSSID (MAC address). /// /// Format: "00:11:22:33:44:55" + #[must_use] pub fn bssid(mut self, bssid: impl Into) -> Self { self.bssid = Some(bssid.into()); self @@ -254,6 +261,7 @@ impl WifiConnectionBuilder { /// .ipv6_ignore() /// .build(); /// ``` + #[must_use] pub fn mode(mut self, mode: WifiMode) -> Self { self.mode = mode; self @@ -262,30 +270,35 @@ impl WifiConnectionBuilder { // Delegation methods to inner ConnectionBuilder /// Applies connection options (autoconnect settings). + #[must_use] pub fn options(mut self, opts: &ConnectionOptions) -> Self { self.inner = self.inner.options(opts); self } /// Enables or disables automatic connection. + #[must_use] pub fn autoconnect(mut self, enabled: bool) -> Self { self.inner = self.inner.autoconnect(enabled); self } /// Sets autoconnect priority (higher values preferred). + #[must_use] pub fn autoconnect_priority(mut self, priority: i32) -> Self { self.inner = self.inner.autoconnect_priority(priority); self } /// Sets autoconnect retry limit. + #[must_use] pub fn autoconnect_retries(mut self, retries: i32) -> Self { self.inner = self.inner.autoconnect_retries(retries); self } /// Configures IPv4 to use DHCP. + #[must_use] pub fn ipv4_auto(mut self) -> Self { self.inner = self.inner.ipv4_auto(); self @@ -295,18 +308,21 @@ impl WifiConnectionBuilder { /// /// This is the typical IPv4 setting for [`WifiMode::Ap`] connections, /// where the device provides network access to connected clients. + #[must_use] pub fn ipv4_shared(mut self) -> Self { self.inner = self.inner.ipv4_shared(); self } /// Configures IPv6 to use SLAAC/DHCPv6. + #[must_use] pub fn ipv6_auto(mut self) -> Self { self.inner = self.inner.ipv6_auto(); self } /// Disables IPv6. + #[must_use] pub fn ipv6_ignore(mut self) -> Self { self.inner = self.inner.ipv6_ignore(); self @@ -316,6 +332,7 @@ impl WifiConnectionBuilder { /// /// This method adds the WiFi-specific "802-11-wireless" section and links /// it to the security section if configured. + #[must_use] pub fn build(mut self) -> HashMap<&'static str, HashMap<&'static str, Value<'static>>> { // Build the 802-11-wireless section let mut wireless = HashMap::new(); diff --git a/nmrs/src/api/builders/wireguard_builder.rs b/nmrs/src/api/builders/wireguard_builder.rs index 164ca2a4..b675ec4f 100644 --- a/nmrs/src/api/builders/wireguard_builder.rs +++ b/nmrs/src/api/builders/wireguard_builder.rs @@ -53,6 +53,7 @@ impl WireGuardBuilder { /// # Arguments /// /// * `name` - Human-readable connection name + #[must_use] pub fn new(name: impl Into) -> Self { let name = name.into(); let inner = ConnectionBuilder::new("wireguard", &name); @@ -72,6 +73,7 @@ impl WireGuardBuilder { /// Sets the WireGuard private key. /// /// The key must be a valid base64-encoded 32-byte WireGuard key (44 characters). + #[must_use] pub fn private_key(mut self, key: impl Into) -> Self { self.private_key = Some(key.into()); self @@ -86,6 +88,7 @@ impl WireGuardBuilder { /// let builder = WireGuardBuilder::new("MyVPN") /// .address("10.0.0.2/24"); /// ``` + #[must_use] pub fn address(mut self, address: impl Into) -> Self { self.address = Some(address.into()); self @@ -94,12 +97,14 @@ impl WireGuardBuilder { /// Adds a WireGuard peer to the connection. /// /// At least one peer must be added before building. + #[must_use] pub fn add_peer(mut self, peer: WireGuardPeer) -> Self { self.peers.push(peer); self } /// Adds multiple WireGuard peers at once. + #[must_use] pub fn add_peers(mut self, peers: impl IntoIterator) -> Self { self.peers.extend(peers); self @@ -114,6 +119,7 @@ impl WireGuardBuilder { /// let builder = WireGuardBuilder::new("MyVPN") /// .dns(vec!["1.1.1.1".into(), "8.8.8.8".into()]); /// ``` + #[must_use] pub fn dns(mut self, servers: Vec) -> Self { self.dns = Some(servers); self @@ -122,6 +128,7 @@ impl WireGuardBuilder { /// Sets the MTU (Maximum Transmission Unit) for the WireGuard interface. /// /// Typical value is 1420 for WireGuard over IPv4. + #[must_use] pub fn mtu(mut self, mtu: u32) -> Self { self.mtu = Some(mtu); self @@ -131,6 +138,7 @@ impl WireGuardBuilder { /// /// If not set, a deterministic UUID will be generated based on the /// connection name. + #[must_use] pub fn uuid(mut self, uuid: Uuid) -> Self { self.uuid = Some(uuid); self @@ -139,24 +147,28 @@ impl WireGuardBuilder { // Delegation methods to inner ConnectionBuilder /// Applies connection options. + #[must_use] pub fn options(mut self, opts: &ConnectionOptions) -> Self { self.inner = self.inner.options(opts); self } /// Enables or disables automatic connection. + #[must_use] pub fn autoconnect(mut self, enabled: bool) -> Self { self.inner = self.inner.autoconnect(enabled); self } /// Sets autoconnect priority. + #[must_use] pub fn autoconnect_priority(mut self, priority: i32) -> Self { self.inner = self.inner.autoconnect_priority(priority); self } /// Sets autoconnect retry limit. + #[must_use] pub fn autoconnect_retries(mut self, retries: i32) -> Self { self.inner = self.inner.autoconnect_retries(retries); self diff --git a/nmrs/src/api/models.rs b/nmrs/src/api/models.rs index 628b80ce..87de7dab 100644 --- a/nmrs/src/api/models.rs +++ b/nmrs/src/api/models.rs @@ -145,6 +145,7 @@ impl Display for ConnectionStateReason { /// /// Maps authentication-related failures to `AuthFailed`, timeout issues to `Timeout`, /// and other failures to the appropriate variant. +#[must_use] pub fn connection_state_reason_to_error(code: u32) -> ConnectionError { let reason = ConnectionStateReason::from(code); match reason { @@ -469,6 +470,7 @@ impl DeviceIdentity { /// /// * `permanent_mac` - The permanent (factory-assigned) MAC address /// * `current_mac` - The current MAC address in use + #[must_use] pub fn new(permanent_mac: String, current_mac: String) -> Self { Self { permanent_mac, @@ -612,41 +614,48 @@ impl EapOptions { /// .system_ca_certs(true) /// .build(); /// ``` + #[must_use] pub fn builder() -> EapOptionsBuilder { EapOptionsBuilder::default() } /// Sets the anonymous identity for privacy. + #[must_use] pub fn with_anonymous_identity(mut self, anonymous_identity: impl Into) -> Self { self.anonymous_identity = Some(anonymous_identity.into()); self } /// Sets the domain suffix to match against the server certificate. + #[must_use] pub fn with_domain_suffix_match(mut self, domain: impl Into) -> Self { self.domain_suffix_match = Some(domain.into()); self } /// Sets the path to the CA certificate file (must start with `file://`). + #[must_use] pub fn with_ca_cert_path(mut self, path: impl Into) -> Self { self.ca_cert_path = Some(path.into()); self } /// Sets whether to use the system CA certificate store. + #[must_use] pub fn with_system_ca_certs(mut self, use_system: bool) -> Self { self.system_ca_certs = use_system; self } /// Sets the EAP method (PEAP or TTLS). + #[must_use] pub fn with_method(mut self, method: EapMethod) -> Self { self.method = method; self } /// Sets the Phase 2 authentication method. + #[must_use] pub fn with_phase2(mut self, phase2: Phase2) -> Self { self.phase2 = phase2; self @@ -705,6 +714,7 @@ impl EapOptionsBuilder { /// Sets the user identity (usually email or username). /// /// This is a required field. + #[must_use] pub fn identity(mut self, identity: impl Into) -> Self { self.identity = Some(identity.into()); self @@ -713,6 +723,7 @@ impl EapOptionsBuilder { /// Sets the password for authentication. /// /// This is a required field. + #[must_use] pub fn password(mut self, password: impl Into) -> Self { self.password = Some(password.into()); self @@ -731,6 +742,7 @@ impl EapOptionsBuilder { /// let builder = EapOptions::builder() /// .anonymous_identity("anonymous@company.com"); /// ``` + #[must_use] pub fn anonymous_identity(mut self, anonymous_identity: impl Into) -> Self { self.anonymous_identity = Some(anonymous_identity.into()); self @@ -749,6 +761,7 @@ impl EapOptionsBuilder { /// let builder = EapOptions::builder() /// .domain_suffix_match("company.com"); /// ``` + #[must_use] pub fn domain_suffix_match(mut self, domain: impl Into) -> Self { self.domain_suffix_match = Some(domain.into()); self @@ -766,6 +779,7 @@ impl EapOptionsBuilder { /// let builder = EapOptions::builder() /// .ca_cert_path("file:///etc/ssl/certs/company-ca.pem"); /// ``` + #[must_use] pub fn ca_cert_path(mut self, path: impl Into) -> Self { self.ca_cert_path = Some(path.into()); self @@ -784,6 +798,7 @@ impl EapOptionsBuilder { /// let builder = EapOptions::builder() /// .system_ca_certs(true); /// ``` + #[must_use] pub fn system_ca_certs(mut self, use_system: bool) -> Self { self.system_ca_certs = use_system; self @@ -802,6 +817,7 @@ impl EapOptionsBuilder { /// let builder = EapOptions::builder() /// .method(EapMethod::Peap); /// ``` + #[must_use] pub fn method(mut self, method: EapMethod) -> Self { self.method = Some(method); self @@ -820,6 +836,7 @@ impl EapOptionsBuilder { /// let builder = EapOptions::builder() /// .phase2(Phase2::Mschapv2); /// ``` + #[must_use] pub fn phase2(mut self, phase2: Phase2) -> Self { self.phase2 = Some(phase2); self @@ -847,6 +864,7 @@ impl EapOptionsBuilder { /// .phase2(Phase2::Mschapv2) /// .build(); /// ``` + #[must_use] pub fn build(self) -> EapOptions { EapOptions { identity: self @@ -923,6 +941,7 @@ impl TimeoutConfig { /// /// let config = TimeoutConfig::new(); /// ``` + #[must_use] pub fn new() -> Self { Self::default() } @@ -942,6 +961,7 @@ impl TimeoutConfig { /// let config = TimeoutConfig::new() /// .with_connection_timeout(Duration::from_secs(60)); /// ``` + #[must_use] pub fn with_connection_timeout(mut self, timeout: Duration) -> Self { self.connection_timeout = timeout; self @@ -961,6 +981,7 @@ impl TimeoutConfig { /// let config = TimeoutConfig::new() /// .with_disconnect_timeout(Duration::from_secs(20)); /// ``` + #[must_use] pub fn with_disconnect_timeout(mut self, timeout: Duration) -> Self { self.disconnect_timeout = timeout; self @@ -1025,6 +1046,7 @@ impl ConnectionOptions { /// /// let opts = ConnectionOptions::new(true); /// ``` + #[must_use] pub fn new(autoconnect: bool) -> Self { Self { autoconnect, @@ -1034,12 +1056,14 @@ impl ConnectionOptions { } /// Sets the auto-connection priority. + #[must_use] pub fn with_priority(mut self, priority: i32) -> Self { self.autoconnect_priority = Some(priority); self } /// Sets the maximum number of auto-connect retry attempts. + #[must_use] pub fn with_retries(mut self, retries: i32) -> Self { self.autoconnect_retries = Some(retries); self @@ -1259,23 +1283,27 @@ impl VpnCredentials { /// .with_dns(vec!["1.1.1.1".into()]) /// .build(); /// ``` + #[must_use] pub fn builder() -> VpnCredentialsBuilder { VpnCredentialsBuilder::default() } /// Sets the DNS servers to use when connected. + #[must_use] pub fn with_dns(mut self, dns: Vec) -> Self { self.dns = Some(dns); self } /// Sets the MTU (Maximum Transmission Unit) size. + #[must_use] pub fn with_mtu(mut self, mtu: u32) -> Self { self.mtu = Some(mtu); self } /// Sets the UUID for the connection. + #[must_use] pub fn with_uuid(mut self, uuid: Uuid) -> Self { self.uuid = Some(uuid); self @@ -1350,6 +1378,7 @@ impl VpnCredentialsBuilder { /// Sets the VPN type to WireGuard. /// /// Currently, WireGuard is the only supported VPN type. + #[must_use] pub fn wireguard(mut self) -> Self { self.vpn_type = Some(VpnType::WireGuard); self @@ -1358,6 +1387,7 @@ impl VpnCredentialsBuilder { /// Sets the VPN type. /// /// For most use cases, prefer using [`wireguard()`](Self::wireguard) instead. + #[must_use] pub fn vpn_type(mut self, vpn_type: VpnType) -> Self { self.vpn_type = Some(vpn_type); self @@ -1366,6 +1396,7 @@ impl VpnCredentialsBuilder { /// Sets the connection name. /// /// This is the unique identifier for the VPN connection profile. + #[must_use] pub fn name(mut self, name: impl Into) -> Self { self.name = Some(name.into()); self @@ -1374,6 +1405,7 @@ impl VpnCredentialsBuilder { /// Sets the VPN gateway endpoint. /// /// Should be in "host:port" format (e.g., "vpn.example.com:51820"). + #[must_use] pub fn gateway(mut self, gateway: impl Into) -> Self { self.gateway = Some(gateway.into()); self @@ -1382,6 +1414,7 @@ impl VpnCredentialsBuilder { /// Sets the client's WireGuard private key. /// /// The private key should be base64 encoded. + #[must_use] pub fn private_key(mut self, private_key: impl Into) -> Self { self.private_key = Some(private_key.into()); self @@ -1393,6 +1426,7 @@ impl VpnCredentialsBuilder { /// /// - "10.0.0.2/24" for a /24 subnet /// - "192.168.1.10/32" for a single IP + #[must_use] pub fn address(mut self, address: impl Into) -> Self { self.address = Some(address.into()); self @@ -1401,6 +1435,7 @@ impl VpnCredentialsBuilder { /// Adds a WireGuard peer to the connection. /// /// Multiple peers can be added by calling this method multiple times. + #[must_use] pub fn add_peer(mut self, peer: WireGuardPeer) -> Self { self.peers.push(peer); self @@ -1409,6 +1444,7 @@ impl VpnCredentialsBuilder { /// Sets all WireGuard peers at once. /// /// This replaces any previously added peers. + #[must_use] pub fn peers(mut self, peers: Vec) -> Self { self.peers = peers; self @@ -1424,6 +1460,7 @@ impl VpnCredentialsBuilder { /// let builder = VpnCredentials::builder() /// .with_dns(vec!["1.1.1.1".into(), "8.8.8.8".into()]); /// ``` + #[must_use] pub fn with_dns(mut self, dns: Vec) -> Self { self.dns = Some(dns); self @@ -1432,6 +1469,7 @@ impl VpnCredentialsBuilder { /// Sets the MTU (Maximum Transmission Unit) size. /// /// Typical values are 1420 for WireGuard over standard networks. + #[must_use] pub fn with_mtu(mut self, mtu: u32) -> Self { self.mtu = Some(mtu); self @@ -1440,6 +1478,7 @@ impl VpnCredentialsBuilder { /// Sets a specific UUID for the connection. /// /// If not set, NetworkManager will generate one automatically. + #[must_use] pub fn with_uuid(mut self, uuid: Uuid) -> Self { self.uuid = Some(uuid); self @@ -1477,6 +1516,7 @@ impl VpnCredentialsBuilder { /// .add_peer(peer) /// .build(); /// ``` + #[must_use] pub fn build(self) -> VpnCredentials { VpnCredentials { vpn_type: self @@ -1568,12 +1608,14 @@ impl WireGuardPeer { } /// Sets the pre-shared key for additional security. + #[must_use] pub fn with_preshared_key(mut self, psk: impl Into) -> Self { self.preshared_key = Some(psk.into()); self } /// Sets the persistent keepalive interval in seconds. + #[must_use] pub fn with_persistent_keepalive(mut self, interval: u32) -> Self { self.persistent_keepalive = Some(interval); self @@ -1788,6 +1830,7 @@ impl BluetoothDevice { /// DeviceState::Activated, /// ); /// ``` + #[must_use] pub fn new( bdaddr: String, name: Option, @@ -1835,6 +1878,7 @@ impl DeviceType { /// /// Currently only WiFi and WiFi P2P devices support scanning. /// For unknown device types, consults the internal device type registry. + #[must_use] pub fn supports_scanning(&self) -> bool { match self { Self::Wifi | Self::WifiP2P => true, @@ -1848,6 +1892,7 @@ impl DeviceType { /// WiFi devices require an access point to connect to, while Ethernet can connect /// without a specific target. /// For unknown device types, consults the internal device type registry. + #[must_use] pub fn requires_specific_object(&self) -> bool { match self { Self::Wifi | Self::WifiP2P => true, @@ -1862,6 +1907,7 @@ impl DeviceType { /// /// WiFi has a global radio killswitch that can enable/disable all WiFi devices. /// For unknown device types, consults the internal device type registry. + #[must_use] pub fn has_global_enabled_state(&self) -> bool { match self { Self::Wifi => true, @@ -1876,6 +1922,7 @@ impl DeviceType { /// /// This is used when creating connection profiles for this device type. /// For unknown device types, consults the internal device type registry. + #[must_use] pub fn connection_type_str(&self) -> &'static str { match self { Self::Ethernet => "802-3-ethernet", @@ -1891,6 +1938,7 @@ impl DeviceType { } /// Returns the raw NetworkManager type code for this device. + #[must_use] pub fn to_code(&self) -> u32 { match self { Self::Ethernet => 1, @@ -1931,16 +1979,19 @@ pub enum DeviceState { impl Device { /// Returns `true` if this is a wired (Ethernet) device. + #[must_use] pub fn is_wired(&self) -> bool { matches!(self.device_type, DeviceType::Ethernet) } /// Returns `true` if this is a wireless (Wi-Fi) device. + #[must_use] pub fn is_wireless(&self) -> bool { matches!(self.device_type, DeviceType::Wifi) } /// Returns 'true' if this is a Bluetooth (DUN or PANU) device. + #[must_use] pub fn is_bluetooth(&self) -> bool { matches!(self.device_type, DeviceType::Bluetooth) } @@ -2273,6 +2324,7 @@ impl Display for StateReason { /// /// Maps authentication-related failures to `AuthFailed`, DHCP issues to `DhcpFailed`, /// and other failures to the appropriate variant. +#[must_use] pub fn reason_to_error(code: u32) -> ConnectionError { let reason = StateReason::from(code); match reason { @@ -2375,16 +2427,19 @@ impl From for BluetoothNetworkRole { impl WifiSecurity { /// Returns `true` if this security type requires authentication. + #[must_use] pub fn secured(&self) -> bool { !matches!(self, WifiSecurity::Open) } /// Returns `true` if this is a WPA-PSK (password-based) security type. + #[must_use] pub fn is_psk(&self) -> bool { matches!(self, WifiSecurity::WpaPsk { .. }) } /// Returns `true` if this is a WPA-EAP (Enterprise/802.1X) security type. + #[must_use] pub fn is_eap(&self) -> bool { matches!(self, WifiSecurity::WpaEap { .. }) } @@ -3100,7 +3155,7 @@ mod tests { fn test_vpn_credentials_builder_missing_name() { let peer = WireGuardPeer::new("key", "vpn.example.com:51820", vec!["0.0.0.0/0".into()]); - VpnCredentials::builder() + let _ = VpnCredentials::builder() .wireguard() .gateway("vpn.example.com:51820") .private_key("private_key") @@ -3114,7 +3169,7 @@ mod tests { fn test_vpn_credentials_builder_missing_vpn_type() { let peer = WireGuardPeer::new("key", "vpn.example.com:51820", vec!["0.0.0.0/0".into()]); - VpnCredentials::builder() + let _ = VpnCredentials::builder() .name("TestVPN") .gateway("vpn.example.com:51820") .private_key("private_key") @@ -3126,7 +3181,7 @@ mod tests { #[test] #[should_panic(expected = "at least one peer is required")] fn test_vpn_credentials_builder_missing_peers() { - VpnCredentials::builder() + let _ = VpnCredentials::builder() .name("TestVPN") .wireguard() .gateway("vpn.example.com:51820") @@ -3219,7 +3274,7 @@ mod tests { #[test] #[should_panic(expected = "identity is required")] fn test_eap_options_builder_missing_identity() { - EapOptions::builder() + let _ = EapOptions::builder() .password("password") .method(EapMethod::Peap) .phase2(Phase2::Mschapv2) @@ -3229,7 +3284,7 @@ mod tests { #[test] #[should_panic(expected = "password is required")] fn test_eap_options_builder_missing_password() { - EapOptions::builder() + let _ = EapOptions::builder() .identity("user@example.com") .method(EapMethod::Peap) .phase2(Phase2::Mschapv2) @@ -3239,7 +3294,7 @@ mod tests { #[test] #[should_panic(expected = "method is required")] fn test_eap_options_builder_missing_method() { - EapOptions::builder() + let _ = EapOptions::builder() .identity("user@example.com") .password("password") .phase2(Phase2::Mschapv2) @@ -3249,7 +3304,7 @@ mod tests { #[test] #[should_panic(expected = "phase2 is required")] fn test_eap_options_builder_missing_phase2() { - EapOptions::builder() + let _ = EapOptions::builder() .identity("user@example.com") .password("password") .method(EapMethod::Peap) diff --git a/nmrs/src/api/network_manager.rs b/nmrs/src/api/network_manager.rs index 226e9a22..d4fb5131 100644 --- a/nmrs/src/api/network_manager.rs +++ b/nmrs/src/api/network_manager.rs @@ -176,6 +176,7 @@ impl NetworkManager { /// # Ok(()) /// # } /// ``` + #[must_use] pub fn timeout_config(&self) -> crate::api::models::TimeoutConfig { self.timeout_config } From 67adf418e38b5ddb29bb809ae9a97a0842e749c3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 28 Feb 2026 16:30:28 +0000 Subject: [PATCH 2/2] chore(nix): fix cargoHash --- package.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.nix b/package.nix index 4b0071bb..c8c27e30 100644 --- a/package.nix +++ b/package.nix @@ -19,7 +19,7 @@ rustPlatform.buildRustPackage { src = ./.; - cargoHash = "sha256-la0SAZqDWfDIFohIkX8XscRqYrR3qHyVpa47uf6aqEI="; + cargoHash = "sha256-D9rd1hHiwke3mrVm0kw0ofwoG8BFpBf+8kqVGT7oBEE="; nativeBuildInputs = [ pkg-config