use std::{collections::HashSet, path::PathBuf, time::Duration};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
pub const NO_PREFERRED_ROUTE_HINT: u32 = 0;
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(default)]
pub struct TransportConfig {
pub peer_to_peer: PeerToPeer,
pub connect: Connect,
pub listen: Listen,
pub global: Global,
}
impl TransportConfig {
pub fn new() -> Self {
Self {
peer_to_peer: PeerToPeer {
bluetooth_le: BluetoothLEConfig::new(),
lan: LanConfig::new(),
awdl: AwdlConfig::new(),
wifi_aware: WifiAwareConfig::new(),
multicast_beta: MulticastBetaConfig::new(),
},
connect: Connect {
tcp_servers: HashSet::new(),
websocket_urls: HashSet::new(),
retry_interval: Duration::from_secs(5),
},
listen: Listen {
tcp: TcpListenConfig::new(),
http: HttpListenConfig::new(),
},
global: Global {
sync_group: 0,
routing_hint: NO_PREFERRED_ROUTE_HINT,
},
}
}
pub fn enable_all_peer_to_peer(&mut self) {
self.peer_to_peer.bluetooth_le.enabled = true;
self.peer_to_peer.lan.enabled = true;
self.peer_to_peer.awdl.enabled = true;
self.peer_to_peer.wifi_aware.enabled = true;
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(default)]
pub struct PeerToPeer {
pub bluetooth_le: BluetoothLEConfig,
pub lan: LanConfig,
pub awdl: AwdlConfig,
pub wifi_aware: WifiAwareConfig,
#[serde(rename = "multicast")]
pub multicast_beta: MulticastBetaConfig,
}
#[serde_as]
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(default)]
pub struct Connect {
pub tcp_servers: HashSet<String>,
pub websocket_urls: HashSet<String>,
#[serde_as(as = "::serde_with::DurationMilliSeconds<u64>")]
pub retry_interval: Duration,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(default)]
pub struct Listen {
pub tcp: TcpListenConfig,
pub http: HttpListenConfig,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(default)]
pub struct Global {
pub sync_group: u32,
pub routing_hint: u32,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(default)]
pub struct HttpListenConfig {
pub enabled: bool,
pub interface_ip: String,
pub port: u16,
pub websocket_sync: bool,
pub tls_key_path: Option<PathBuf>,
pub tls_certificate_path: Option<PathBuf>,
}
impl HttpListenConfig {
pub fn new() -> Self {
Self {
enabled: false,
interface_ip: "[::]".to_string(),
port: 80,
websocket_sync: true,
tls_key_path: None,
tls_certificate_path: None,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(default)]
pub struct TcpListenConfig {
pub enabled: bool,
pub interface_ip: String,
pub port: u16,
}
impl TcpListenConfig {
pub fn new() -> Self {
Self {
enabled: false,
interface_ip: "[::]".to_string(),
port: 4040,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(default)]
pub struct BluetoothLEConfig {
pub enabled: bool,
}
impl BluetoothLEConfig {
pub fn new() -> Self {
Self { enabled: false }
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(default)]
pub struct LanConfig {
pub enabled: bool,
pub mdns_enabled: bool,
pub multicast_enabled: bool,
}
impl LanConfig {
pub fn new() -> Self {
Self {
enabled: false,
mdns_enabled: true,
multicast_enabled: true,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(default)]
pub struct AwdlConfig {
pub enabled: bool,
}
impl AwdlConfig {
pub fn new() -> Self {
Self { enabled: false }
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
#[serde(default)]
pub struct WifiAwareConfig {
pub enabled: bool,
}
impl WifiAwareConfig {
pub fn new() -> Self {
Self { enabled: false }
}
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(default)]
pub struct MulticastBetaConfig {
pub enabled: bool,
pub group_address: String,
pub port: u16,
pub interface: Option<String>,
}
impl MulticastBetaConfig {
pub fn new() -> Self {
Self::default()
}
}
impl Default for MulticastBetaConfig {
fn default() -> Self {
Self {
enabled: false,
group_address: "224.1.2.3".to_string(),
port: 6003,
interface: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn awdl_config_default_disabled() {
assert!(!AwdlConfig::default().enabled);
assert!(!AwdlConfig::new().enabled);
}
#[test]
fn wifi_aware_config_default_disabled() {
assert!(!WifiAwareConfig::default().enabled);
assert!(!WifiAwareConfig::new().enabled);
}
#[test]
fn enable_all_peer_to_peer_enables_all_transports() {
let mut config = TransportConfig::new();
config.enable_all_peer_to_peer();
assert!(config.peer_to_peer.bluetooth_le.enabled);
assert!(config.peer_to_peer.lan.enabled);
assert!(config.peer_to_peer.awdl.enabled);
assert!(config.peer_to_peer.wifi_aware.enabled);
}
#[test]
fn enable_all_peer_to_peer_does_not_affect_multicast() {
for multicast_enabled in [false, true] {
let mut config = TransportConfig::new();
config.peer_to_peer.multicast_beta.enabled = multicast_enabled;
config.enable_all_peer_to_peer();
assert_eq!(
config.peer_to_peer.multicast_beta.enabled,
multicast_enabled
);
}
}
#[test]
fn awdl_config_serde_roundtrip() {
let original = AwdlConfig { enabled: true };
let json = serde_json::to_string(&original).unwrap();
let decoded: AwdlConfig = serde_json::from_str(&json).unwrap();
assert_eq!(original, decoded);
}
#[test]
fn wifi_aware_config_serde_roundtrip() {
let original = WifiAwareConfig { enabled: true };
let json = serde_json::to_string(&original).unwrap();
let decoded: WifiAwareConfig = serde_json::from_str(&json).unwrap();
assert_eq!(original, decoded);
}
#[test]
fn peer_to_peer_missing_fields_default_to_disabled() {
let json = r#"{"bluetooth_le":{"enabled":true},"lan":{"enabled":true,"mdns_enabled":true,"multicast_enabled":true}}"#;
let peer_to_peer: PeerToPeer = serde_json::from_str(json).unwrap();
assert!(!peer_to_peer.awdl.enabled);
assert!(!peer_to_peer.wifi_aware.enabled);
}
#[test]
fn multicast_beta_config_default_disabled() {
let cfg = MulticastBetaConfig::default();
assert!(!cfg.enabled);
assert_eq!(cfg.group_address, "224.1.2.3");
assert_eq!(cfg.port, 6003);
assert!(cfg.interface.is_none());
}
#[test]
fn multicast_beta_config_new_equals_default() {
assert_eq!(MulticastBetaConfig::new(), MulticastBetaConfig::default());
}
#[test]
fn multicast_beta_config_serde_roundtrip() {
let original = MulticastBetaConfig {
enabled: true,
group_address: "224.1.2.3".to_string(),
port: 6003,
interface: Some("en0".to_string()),
};
let json = serde_json::to_string(&original).unwrap();
let decoded: MulticastBetaConfig = serde_json::from_str(&json).unwrap();
assert_eq!(original, decoded);
}
#[test]
fn transport_config_peer_to_peer_exposes_multicast_beta() {
let mut config = TransportConfig::new();
config.peer_to_peer.multicast_beta.enabled = true;
config.peer_to_peer.multicast_beta.group_address = "224.5.6.7".to_string();
config.peer_to_peer.multicast_beta.port = 7000;
assert!(config.peer_to_peer.multicast_beta.enabled);
assert_eq!(
config.peer_to_peer.multicast_beta.group_address,
"224.5.6.7"
);
assert_eq!(config.peer_to_peer.multicast_beta.port, 7000);
let json = serde_json::to_value(&config.peer_to_peer).unwrap();
assert!(json.get("multicast").is_some());
assert!(json.get("multicast_beta").is_none());
}
}