netwatch_rs/platform/
linux.rs1use crate::{
2 device::{NetworkReader, NetworkStats},
3 error::{NetwatchError, Result},
4};
5use std::fs;
6use std::time::SystemTime;
7
8pub struct LinuxReader;
9
10impl Default for LinuxReader {
11 fn default() -> Self {
12 Self::new()
13 }
14}
15
16impl LinuxReader {
17 pub fn new() -> Self {
18 Self
19 }
20
21 fn parse_proc_net_dev(&self, content: &str, device: &str) -> Result<NetworkStats> {
22 for line in content.lines().skip(2) {
23 let parts: Vec<&str> = line.split_whitespace().collect();
24 if parts.is_empty() {
25 continue;
26 }
27
28 let iface_name = parts[0].trim_end_matches(':');
29 if iface_name == device {
30 return Ok(NetworkStats {
31 timestamp: SystemTime::now(),
32 bytes_in: parts.get(1).unwrap_or(&"0").parse().unwrap_or(0),
33 packets_in: parts.get(2).unwrap_or(&"0").parse().unwrap_or(0),
34 errors_in: parts.get(3).unwrap_or(&"0").parse().unwrap_or(0),
35 drops_in: parts.get(4).unwrap_or(&"0").parse().unwrap_or(0),
36 bytes_out: parts.get(9).unwrap_or(&"0").parse().unwrap_or(0),
37 packets_out: parts.get(10).unwrap_or(&"0").parse().unwrap_or(0),
38 errors_out: parts.get(11).unwrap_or(&"0").parse().unwrap_or(0),
39 drops_out: parts.get(12).unwrap_or(&"0").parse().unwrap_or(0),
40 });
41 }
42 }
43
44 Err(NetwatchError::DeviceNotFound(device.to_string()))
45 }
46}
47
48impl NetworkReader for LinuxReader {
49 fn list_devices(&self) -> Result<Vec<String>> {
50 let content = fs::read_to_string("/proc/net/dev")?;
51 let mut devices = Vec::new();
52
53 for line in content.lines().skip(2) {
54 if let Some(device_part) = line.split(':').next() {
55 let device_name = device_part.trim().to_string();
56 if !device_name.is_empty() {
57 devices.push(device_name);
58 }
59 }
60 }
61
62 devices.retain(|name| {
64 !name.starts_with("lo")
65 && !name.starts_with("docker")
66 && !name.starts_with("veth")
67 && !name.starts_with("br-")
68 });
69
70 Ok(devices)
71 }
72
73 fn read_stats(&self, device: &str) -> Result<NetworkStats> {
74 let content = fs::read_to_string("/proc/net/dev")?;
75 self.parse_proc_net_dev(&content, device)
76 }
77
78 fn is_available(&self) -> bool {
79 std::path::Path::new("/proc/net/dev").exists()
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_parse_proc_net_dev() {
89 let reader = LinuxReader::new();
90 let sample_data = r#"Inter-| Receive | Transmit
91 face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
92 lo: 1234567 100 0 0 0 0 0 0 1234567 100 0 0 0 0 0 0
93 eth0: 9876543210 5000 0 0 0 0 0 0 1234567890 3000 0 0 0 0 0 0
94"#;
95
96 let stats = reader.parse_proc_net_dev(sample_data, "eth0").unwrap();
97 assert_eq!(stats.bytes_in, 9876543210);
98 assert_eq!(stats.bytes_out, 1234567890);
99 assert_eq!(stats.packets_in, 5000);
100 assert_eq!(stats.packets_out, 3000);
101 }
102
103 #[test]
104 fn test_device_not_found() {
105 let reader = LinuxReader::new();
106 let sample_data = r#"Inter-| Receive | Transmit
107 face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
108 lo: 1234567 100 0 0 0 0 0 0 1234567 100 0 0 0 0 0 0
109"#;
110
111 let result = reader.parse_proc_net_dev(sample_data, "nonexistent");
112 assert!(result.is_err());
113 assert!(matches!(
114 result.unwrap_err(),
115 NetwatchError::DeviceNotFound(_)
116 ));
117 }
118}