use std::fs;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::error::Result;
use crate::models::{NetworkStats, BandwidthPoint};
pub struct NetworkStatsTracker {
interface: String,
last_rx_bytes: u64,
last_tx_bytes: u64,
last_check_time: u64,
start_rx_bytes: u64,
start_tx_bytes: u64,
start_time: u64,
}
impl NetworkStatsTracker {
pub fn new(interface: String) -> Result<Self> {
let (rx_bytes, tx_bytes) = Self::read_interface_stats(&interface)?;
let now = Self::current_timestamp();
Ok(Self {
interface,
last_rx_bytes: rx_bytes,
last_tx_bytes: tx_bytes,
last_check_time: now,
start_rx_bytes: rx_bytes,
start_tx_bytes: tx_bytes,
start_time: now,
})
}
pub fn get_stats(&mut self) -> Result<NetworkStats> {
let (rx_bytes, tx_bytes) = Self::read_interface_stats(&self.interface)?;
let now = Self::current_timestamp();
let time_delta = (now - self.last_check_time) as f64;
let download_speed = if time_delta > 0.0 {
((rx_bytes - self.last_rx_bytes) as f64 / time_delta) as u64
} else {
0
};
let upload_speed = if time_delta > 0.0 {
((tx_bytes - self.last_tx_bytes) as f64 / time_delta) as u64
} else {
0
};
self.last_rx_bytes = rx_bytes;
self.last_tx_bytes = tx_bytes;
self.last_check_time = now;
Ok(NetworkStats {
download_speed,
upload_speed,
total_downloaded: rx_bytes - self.start_rx_bytes,
total_uploaded: tx_bytes - self.start_tx_bytes,
connection_duration: now - self.start_time,
interface: self.interface.clone(),
})
}
pub fn get_bandwidth_point(&mut self) -> Result<BandwidthPoint> {
let stats = self.get_stats()?;
Ok(BandwidthPoint {
timestamp: Self::current_timestamp(),
download_speed: stats.download_speed,
upload_speed: stats.upload_speed,
})
}
fn read_interface_stats(interface: &str) -> Result<(u64, u64)> {
let rx_path = format!("/sys/class/net/{}/statistics/rx_bytes", interface);
let tx_path = format!("/sys/class/net/{}/statistics/tx_bytes", interface);
let rx_bytes = fs::read_to_string(&rx_path)
.map_err(|e| crate::error::NetworkError::OperationError(
format!("Failed to read rx_bytes for {}: {}", interface, e)
))?
.trim()
.parse::<u64>()
.map_err(|e| crate::error::NetworkError::OperationError(
format!("Failed to parse rx_bytes: {}", e)
))?;
let tx_bytes = fs::read_to_string(&tx_path)
.map_err(|e| crate::error::NetworkError::OperationError(
format!("Failed to read tx_bytes for {}: {}", interface, e)
))?
.trim()
.parse::<u64>()
.map_err(|e| crate::error::NetworkError::OperationError(
format!("Failed to parse tx_bytes: {}", e)
))?;
Ok((rx_bytes, tx_bytes))
}
fn current_timestamp() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs()
}
}
pub fn get_network_interfaces() -> Result<Vec<String>> {
let net_path = "/sys/class/net";
let entries = fs::read_dir(net_path)
.map_err(|e| crate::error::NetworkError::OperationError(
format!("Failed to read {}: {}", net_path, e)
))?;
let mut interfaces = Vec::new();
for entry in entries {
if let Ok(entry) = entry {
if let Some(name) = entry.file_name().to_str() {
if name != "lo" {
interfaces.push(name.to_string());
}
}
}
}
Ok(interfaces)
}