Architecture
This page describes the internal architecture of the nmrs library. Understanding this helps when contributing or debugging.
Crate Structure
nmrs/src/
├── lib.rs # Crate root: re-exports, Result type alias
├── api/ # Public API layer
│ ├── mod.rs
│ ├── network_manager.rs # NetworkManager struct and methods
│ ├── models/ # Data types (Device, Network, etc.)
│ │ ├── mod.rs
│ │ ├── device.rs
│ │ ├── wifi.rs
│ │ ├── vpn.rs
│ │ ├── bluetooth.rs
│ │ ├── config.rs
│ │ ├── error.rs
│ │ ├── connection_state.rs
│ │ └── state_reason.rs
│ └── builders/ # Connection settings builders
│ ├── mod.rs
│ ├── connection_builder.rs
│ ├── wifi.rs
│ ├── wifi_builder.rs
│ ├── vpn.rs
│ ├── wireguard_builder.rs
│ └── bluetooth.rs
├── core/ # Business logic
│ ├── mod.rs
│ ├── connection.rs # Wi-Fi/Ethernet connect/disconnect
│ ├── connection_settings.rs # Saved connection management
│ ├── device.rs # Device listing, Wi-Fi control
│ ├── scan.rs # Wi-Fi scanning
│ ├── vpn.rs # VPN connect/disconnect/list
│ ├── bluetooth.rs # Bluetooth connections
│ └── state_wait.rs # Wait for state transitions
├── dbus/ # D-Bus proxy types
│ ├── mod.rs
│ ├── main_nm.rs # NetworkManager proxy
│ ├── device.rs # Device proxy
│ ├── wireless.rs # Wireless device proxy
│ ├── access_point.rs # Access point proxy
│ ├── active_connection.rs # Active connection proxy
│ ├── wired.rs # Wired device proxy
│ └── bluetooth.rs # Bluetooth device proxy
├── monitoring/ # D-Bus signal monitoring
│ ├── mod.rs
│ ├── network.rs # AP added/removed signals
│ ├── device.rs # Device state change signals
│ ├── wifi.rs # Current connection info
│ ├── bluetooth.rs # Bluetooth signals
│ ├── info.rs # Network detail retrieval
│ └── transport.rs # Signal transport
├── types/ # Constants and registries
│ ├── mod.rs
│ ├── constants.rs # NM device type codes
│ └── device_type_registry.rs # Device type capabilities
└── util/ # Utilities
├── mod.rs
├── utils.rs # Channel calculation, SSID decoding, etc.
└── validation.rs # Input validation
Layer Architecture
┌──────────────────────────────────────────────────────────┐
│ Your Application │
├──────────────────────────────────────────────────────────┤
│ api/network_manager.rs ← Public API (NetworkManager) │
│ api/models/ ← Public data types │
│ api/builders/ ← Public connection builders │
├──────────────────────────────────────────────────────────┤
│ core/ ← Business logic (not public) │
│ monitoring/ ← Signal monitoring (not public) │
├──────────────────────────────────────────────────────────┤
│ dbus/ ← D-Bus proxy types (not public) │
│ util/ ← Utilities (not public) │
│ types/ ← Constants (not public) │
├──────────────────────────────────────────────────────────┤
│ zbus ← D-Bus library │
├──────────────────────────────────────────────────────────┤
│ D-Bus System Bus → NetworkManager Daemon │
└──────────────────────────────────────────────────────────┘
API Layer
The api module defines the public interface:
NetworkManagerdelegates tocorefunctionsmodelsdefine all public data typesbuildersconstruct NM settings dictionaries
Core Layer
The core module contains the actual business logic:
connection.rshandles Wi-Fi/Ethernet connect/disconnectscan.rshandles network scanning and listingvpn.rshandles WireGuard VPN operationsstate_wait.rsuses D-Bus signals to wait for state transitions
D-Bus Layer
The dbus module defines typed proxy structs generated with zbus::proxy macros. Each proxy corresponds to a NetworkManager D-Bus interface.
Monitoring Layer
The monitoring module subscribes to D-Bus signals for real-time updates:
- Network list changes (AP added/removed)
- Device state changes
- Active connection state
Key Design Decisions
Signal-Based State Waiting
Instead of polling, nmrs uses D-Bus signals to wait for state transitions. When you call connect(), it:
- Sends the
AddAndActivateConnectionD-Bus call - Subscribes to
StateChangedsignals on the device - Awaits the signal with a timeout
- Returns success on
Activated, or maps the failure reason to aConnectionError
This is more efficient and responsive than polling.
Non-Exhaustive Types
All public enums and structs are #[non_exhaustive]. This allows adding new fields, variants, and error types without breaking downstream code.
Connection Reuse
When connecting to a network, nmrs checks for an existing saved profile first. If found, it activates the saved profile rather than creating a new one. This preserves user settings and avoids duplicate profiles.
Validation
Input validation happens at two levels:
- Model constructors (e.g.,
BluetoothIdentity::new()validates MAC format) - Builder build methods (e.g.,
WireGuardBuilder::build()validates keys and addresses)
Next Steps
- Testing – how to run tests
- Contributing – development workflow