Skip to content

Commit dfab11a

Browse files
committed
refactor(#289): connect_vpn sig and new VpnConfig trait
1 parent 0bfae23 commit dfab11a

11 files changed

Lines changed: 339 additions & 81 deletions

File tree

nmrs/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
All notable changes to the `nmrs` crate will be documented in this file.
44

55
## [Unreleased]
6+
### Changed
7+
- Introduce `VpnConfig` trait and refactor `connect_vpn` signature ([#303](https://github.com/cachebag/nmrs/pull/303))
68

79
## [2.2.0] - 2026-03-17
810
### Added

nmrs/examples/vpn_connect.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/// Connect to a WireGuard VPN using NetworkManager and print the assigned IP address.
22
///
3-
/// This example demonstrates using the builder pattern for creating VPN credentials,
4-
/// which provides a more ergonomic and readable API compared to the traditional constructor.
5-
use nmrs::{NetworkManager, VpnCredentials, WireGuardPeer};
3+
/// This example demonstrates creating a `WireGuardConfig`,
4+
/// the preferred API for configuring VPN connections.
5+
use nmrs::{NetworkManager, WireGuardConfig, WireGuardPeer};
66

77
#[tokio::main]
88
async fn main() -> nmrs::Result<()> {
@@ -16,16 +16,14 @@ async fn main() -> nmrs::Result<()> {
1616
)
1717
.with_persistent_keepalive(25);
1818

19-
// Use the builder pattern for a more readable configuration
20-
let creds = VpnCredentials::builder()
21-
.name("ExampleVPN")
22-
.wireguard()
23-
.gateway("vpn.example.com:51820")
24-
.private_key(std::env::var("WG_PRIVATE_KEY").expect("Set WG_PRIVATE_KEY env var"))
25-
.address("10.0.0.2/24")
26-
.add_peer(peer)
27-
.with_dns(vec!["1.1.1.1".into()])
28-
.build();
19+
let creds = WireGuardConfig::new(
20+
"ExampleVPN",
21+
"vpn.example.com:51820",
22+
std::env::var("WG_PRIVATE_KEY").expect("Set WG_PRIVATE_KEY env var"),
23+
"10.0.0.2/24",
24+
vec![peer],
25+
)
26+
.with_dns(vec!["1.1.1.1".into()]);
2927

3028
println!("Connecting to VPN...");
3129
nm.connect_vpn(creds).await?;

nmrs/src/api/builders/vpn.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
//! let settings = build_wireguard_connection(&creds, &opts).unwrap();
6363
//! // Pass settings to NetworkManager's AddAndActivateConnection
6464
//! ```
65+
#![allow(deprecated)]
6566

6667
use std::collections::HashMap;
6768
use zvariant::Value;

nmrs/src/api/models/tests.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(deprecated)]
2+
13
use std::time::Duration;
24
use uuid::Uuid;
35

@@ -616,6 +618,93 @@ fn test_vpn_credentials_builder_basic() {
616618
assert!(creds.mtu.is_none());
617619
}
618620

621+
#[test]
622+
fn test_wireguard_config_basic() {
623+
let peer = WireGuardPeer::new(
624+
"HIgo9xNzJMWLKAShlKl6/bUT1VI9Q0SDBXGtLXkPFXc=",
625+
"vpn.example.com:51820",
626+
vec!["0.0.0.0/0".into()],
627+
);
628+
629+
let config = WireGuardConfig::new(
630+
"TestVPN",
631+
"vpn.example.com:51820",
632+
"YBk6X3pP8KjKz7+HFWzVHNqL3qTZq8hX9VxFQJ4zVmM=",
633+
"10.0.0.2/24",
634+
vec![peer],
635+
);
636+
637+
assert_eq!(config.name, "TestVPN");
638+
assert_eq!(config.gateway, "vpn.example.com:51820");
639+
assert_eq!(
640+
config.private_key,
641+
"YBk6X3pP8KjKz7+HFWzVHNqL3qTZq8hX9VxFQJ4zVmM="
642+
);
643+
assert_eq!(config.address, "10.0.0.2/24");
644+
assert_eq!(config.peers.len(), 1);
645+
assert!(config.dns.is_none());
646+
assert!(config.mtu.is_none());
647+
}
648+
649+
#[test]
650+
fn test_wireguard_config_implements_vpn_config() {
651+
let uuid = Uuid::new_v4();
652+
let config = WireGuardConfig::new(
653+
"TestVPN",
654+
"vpn.example.com:51820",
655+
"private_key",
656+
"10.0.0.2/24",
657+
vec![WireGuardPeer::new(
658+
"public_key",
659+
"vpn.example.com:51820",
660+
vec!["0.0.0.0/0".into()],
661+
)],
662+
)
663+
.with_dns(vec!["1.1.1.1".into(), "8.8.8.8".into()])
664+
.with_mtu(1420)
665+
.with_uuid(uuid);
666+
667+
let vpn_config: &dyn VpnConfig = &config;
668+
669+
assert_eq!(vpn_config.vpn_type(), VpnType::WireGuard);
670+
assert_eq!(vpn_config.name(), "TestVPN");
671+
assert_eq!(vpn_config.gateway(), "vpn.example.com:51820");
672+
assert_eq!(
673+
vpn_config.dns(),
674+
Some(["1.1.1.1".to_string(), "8.8.8.8".to_string()].as_slice())
675+
);
676+
assert_eq!(vpn_config.mtu(), Some(1420));
677+
assert_eq!(vpn_config.uuid(), Some(uuid));
678+
}
679+
680+
#[test]
681+
fn test_wireguard_config_roundtrips_through_vpn_credentials() {
682+
let config = WireGuardConfig::new(
683+
"TestVPN",
684+
"vpn.example.com:51820",
685+
"private_key",
686+
"10.0.0.2/24",
687+
vec![WireGuardPeer::new(
688+
"public_key",
689+
"vpn.example.com:51820",
690+
vec!["0.0.0.0/0".into()],
691+
)],
692+
)
693+
.with_dns(vec!["1.1.1.1".into()])
694+
.with_mtu(1420);
695+
696+
let legacy: VpnCredentials = config.clone().into();
697+
let roundtrip = WireGuardConfig::from(legacy);
698+
699+
assert_eq!(roundtrip.name, config.name);
700+
assert_eq!(roundtrip.gateway, config.gateway);
701+
assert_eq!(roundtrip.private_key, config.private_key);
702+
assert_eq!(roundtrip.address, config.address);
703+
assert_eq!(roundtrip.peers.len(), config.peers.len());
704+
assert_eq!(roundtrip.dns, config.dns);
705+
assert_eq!(roundtrip.mtu, config.mtu);
706+
}
707+
619708
#[test]
620709
fn test_vpn_credentials_builder_with_optionals() {
621710
let peer = WireGuardPeer::new(

0 commit comments

Comments
 (0)