ifstat_rs/net_stats/
linux_impl.rs

1// network_utils.rs
2// This module provides utility functions to retrieve network device statistics
3// and map device strings to friendly names on a Unix-based system.
4
5use std::collections::HashMap;
6use std::fs::File;
7use std::io::{BufRead, BufReader};
8
9use indexmap::IndexMap;
10
11use crate::test_debug;
12
13/// Retrieves network device statistics from the `/proc/net/dev` file.
14///
15/// # Returns
16///
17/// A result containing an IndexMap where the keys are the device names and the values are tuples of (received bytes, transmitted bytes).
18/// In case of an error, returns an io::Error.
19pub fn get_net_dev_stats() -> Result<IndexMap<String, (u64, u64)>, std::io::Error> {
20    // Open the `/proc/net/dev` file for reading
21    let file = File::open("/proc/net/dev")?;
22    let reader = BufReader::new(file);
23    // Parse the network device statistics from the file
24    parse_net_dev_stats(reader)
25}
26
27/// Parses network device statistics from a given reader.
28///
29/// # Arguments
30///
31/// * `reader` - A reader that provides lines of network device statistics.
32///
33/// # Returns
34///
35/// A result containing an IndexMap where the keys are the device names and the values are tuples of (received bytes, transmitted bytes).
36/// In case of an error, returns an io::Error.
37pub fn parse_net_dev_stats<R: BufRead>(
38    reader: R,
39) -> Result<IndexMap<String, (u64, u64)>, std::io::Error> {
40    let mut stats = IndexMap::new();
41    let lines: Vec<_> = reader.lines().collect::<Result<_, _>>()?;
42    test_debug!("Parsing {} lines", lines.len());
43
44    // Skip the first two lines as they are headers
45    for (_index, line) in lines.into_iter().enumerate().skip(2) {
46        test_debug!("Parsing line: {}", line);
47        // Split the line into interface name and the rest of the statistics
48        if let Some((iface, rest)) = line.split_once(':') {
49            let fields: Vec<&str> = rest.split_whitespace().collect();
50            if fields.len() >= 9 {
51                // Parse the received and transmitted bytes
52                let rx_bytes: u64 = fields[0].parse().map_err(|_| {
53                    std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid RX bytes")
54                })?;
55                let tx_bytes: u64 = fields[8].parse().map_err(|_| {
56                    std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid TX bytes")
57                })?;
58                stats.insert(iface.trim().to_string(), (rx_bytes, tx_bytes));
59            } else {
60                test_debug!(
61                    "Invalid line format: '{}' ({} fields: {:?})",
62                    line,
63                    fields.len(),
64                    fields
65                );
66                return Err(std::io::Error::new(
67                    std::io::ErrorKind::InvalidData,
68                    format!("Invalid line format: {} fields", fields.len()),
69                ));
70            }
71        } else {
72            test_debug!("Invalid line format: '{}' (no colon found)", line);
73            return Err(std::io::Error::new(
74                std::io::ErrorKind::InvalidData,
75                "Invalid line format (no colon found)",
76            ));
77        }
78    }
79    Ok(stats)
80}
81
82/// Retrieves a map of device strings to friendly names.
83///
84/// # Returns
85///
86/// A HashMap where the keys are device strings and the values are friendly names.
87pub fn get_device_string_to_name_map() -> HashMap<String, String> {
88    HashMap::new() // This isn't really crucial on linux but we *could* implement it.
89}