use std::sync::mpsc;
#[derive(Debug, Clone)]
pub enum NetworkEvent {
GatewayChanged {
old: Option<String>,
new: Option<String>,
},
}
#[cfg(target_os = "macos")]
fn get_default_gateway() -> Option<String> {
let output = std::process::Command::new("route")
.args(["get", "default"])
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::null())
.output()
.ok()?;
let text = String::from_utf8_lossy(&output.stdout);
for line in text.lines() {
let trimmed = line.trim();
if let Some(gw) = trimmed.strip_prefix("gateway:") {
let gw = gw.trim();
if !gw.is_empty() {
return Some(gw.to_string());
}
}
}
None
}
#[cfg(target_os = "linux")]
fn get_default_gateway() -> Option<String> {
let output = std::process::Command::new("ip")
.args(["route", "show", "default"])
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::null())
.output()
.ok()?;
let text = String::from_utf8_lossy(&output.stdout);
for line in text.lines() {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 3 && parts[0] == "default" && parts[1] == "via" {
return Some(parts[2].to_string());
}
}
None
}
#[must_use]
pub fn spawn_network_monitor(poll_interval: std::time::Duration) -> mpsc::Receiver<NetworkEvent> {
let (tx, rx) = mpsc::channel();
std::thread::spawn(move || {
let mut last_gateway = get_default_gateway();
loop {
std::thread::sleep(poll_interval);
let current = get_default_gateway();
if current != last_gateway {
let event = NetworkEvent::GatewayChanged {
old: last_gateway.clone(),
new: current.clone(),
};
if tx.send(event).is_err() {
break; }
last_gateway = current;
}
}
});
rx
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_default_gateway_returns_some_or_none() {
let _gw = get_default_gateway();
}
}