use sysinfo::Networks;
use std::collections::HashMap;
use std::time::Instant;
#[derive(Clone, Debug)]
pub struct Snapshot {
pub elapsed_secs: f64,
pub bytes_recv: u64,
pub bytes_sent: u64,
}
#[derive(Clone, Debug)]
pub struct DeviceInfo {
pub name: String,
pub addrs: Vec<String>,
}
pub struct Collector {
networks: Networks,
start: Instant,
}
impl Collector {
pub fn new() -> Self {
Self {
networks: Networks::new_with_refreshed_list(),
start: Instant::now(),
}
}
pub fn elapsed_secs(&self) -> f64 {
self.start.elapsed().as_secs_f64()
}
pub fn print_debug_info(&self) {
println!("\n=== Network Interfaces Debug Info ===");
println!("Total interfaces detected by sysinfo: {}\n", self.networks.len());
for (name, data) in self.networks.iter() {
println!("Interface: {}", name);
println!(" MAC address: {}", data.mac_address());
println!(" Total received: {} bytes", data.total_received());
println!(" Total transmitted: {} bytes", data.total_transmitted());
println!(" IP networks:");
let ip_networks = data.ip_networks();
if ip_networks.is_empty() {
println!(" (none)");
} else {
for ip in ip_networks {
println!(" - {} (prefix: {})", ip.addr, ip.prefix);
}
}
println!();
}
println!("Filtered devices (IPv4 only, used in UI): {}\n", self.devices().len());
for dev in self.devices() {
println!(" - {} [{}]", dev.name, dev.addrs.join(", "));
}
#[cfg(target_os = "windows")]
{
println!("\nNote: Windows loopback (127.0.0.1) traffic is not visible via");
println!(" standard network APIs. The Loopback device appears in the");
println!(" list but may show zero traffic.");
}
}
pub fn devices(&self) -> Vec<DeviceInfo> {
let mut devs: Vec<DeviceInfo> = self
.networks
.iter()
.map(|(name, data)| {
let addrs: Vec<String> = data
.ip_networks()
.iter()
.filter(|n| n.addr.is_ipv4())
.map(|n| n.addr.to_string())
.collect();
DeviceInfo {
name: name.to_string(),
addrs,
}
})
.collect();
#[cfg(target_os = "windows")]
{
let has_loopback = devs.iter().any(|d| {
d.name.to_lowercase().contains("loopback")
|| d.addrs.iter().any(|a| a.starts_with("127."))
});
if !has_loopback {
devs.push(DeviceInfo {
name: "Loopback Pseudo-Interface 1".to_string(),
addrs: vec!["127.0.0.1".to_string()],
});
}
}
devs.sort_by(|a, b| a.name.cmp(&b.name));
devs
}
pub fn collect(&mut self) -> HashMap<String, Snapshot> {
self.networks.refresh();
let elapsed = self.start.elapsed().as_secs_f64();
#[cfg(target_os = "windows")]
let mut snapshots: HashMap<String, Snapshot> = self.networks
.iter()
.map(|(name, data)| {
(
name.to_string(),
Snapshot {
elapsed_secs: elapsed,
bytes_recv: data.total_received(),
bytes_sent: data.total_transmitted(),
},
)
})
.collect();
#[cfg(not(target_os = "windows"))]
let snapshots: HashMap<String, Snapshot> = self.networks
.iter()
.map(|(name, data)| {
(
name.to_string(),
Snapshot {
elapsed_secs: elapsed,
bytes_recv: data.total_received(),
bytes_sent: data.total_transmitted(),
},
)
})
.collect();
#[cfg(target_os = "windows")]
{
let has_loopback_snapshot = snapshots.keys().any(|k| {
k.to_lowercase().contains("loopback")
});
if !has_loopback_snapshot {
snapshots.insert(
"Loopback Pseudo-Interface 1".to_string(),
Snapshot {
elapsed_secs: elapsed,
bytes_recv: 0,
bytes_sent: 0,
},
);
}
}
snapshots
}
}