use regex::*;
#[derive(Clone, Debug)]
pub enum StatType {
All,
Bytes,
Packets,
}
#[derive(Clone)]
pub struct Config {
pub filter: Option<FilterOpts>,
pub counters: Option<StatType>,
pub print_total: Option<bool>,
pub format: Option<String>,
}
impl Config {
pub fn new() -> Config {
Config {
filter: None,
counters: None,
print_total: None,
format: None,
}
}
}
impl Default for Config {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone)]
pub struct FilterOpts {
pub include: bool,
pub name: Regex,
}
impl FilterOpts {
pub fn new() -> FilterOpts {
FilterOpts {
include: true,
name: Regex::new(".").expect("cannot create regex"),
}
}
pub fn create(text: &str, include: bool) -> Option<FilterOpts> {
let r = match Regex::new(text) {
Ok(x) => x,
Err(_) => {
return None;
}
};
let filter = FilterOpts { name: r, include };
Some(filter)
}
pub fn matches(self, text: &str) -> bool {
if self.name.is_match(text) && !self.include {
return false;
}
if !self.name.is_match(text) && self.include {
return false;
}
true
}
}
impl Default for FilterOpts {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct Device {
pub name: String,
pub ibytes: i64,
pub obytes: i64,
pub ipackets: i64,
pub opackets: i64,
}
#[derive(Debug)]
pub struct DeviceHist {
pub history: Vec<Device>,
}
#[cfg(target_os = "linux")]
pub enum ProcNetDevIndexes {
Ibytes = 1,
Ipackets = 2,
Obytes = 9,
Opackets = 10,
}
#[allow(clippy::result_unit_err)]
#[cfg(target_os = "linux")]
pub fn line_to_device_struct(line: &str) -> Result<Device, ()> {
let idx = line.find(':');
if idx.is_none() {
return Err(());
}
let part_split = line[idx.unwrap()..].split_ascii_whitespace();
let vec_list = part_split.collect::<Vec<&str>>();
let d = Device {
name: (line[0..idx.unwrap()].trim()).to_string(),
ibytes: vec_list[ProcNetDevIndexes::Ibytes as usize]
.parse()
.unwrap_or(0),
obytes: vec_list[ProcNetDevIndexes::Obytes as usize]
.parse()
.unwrap_or(0),
ipackets: vec_list[ProcNetDevIndexes::Ipackets as usize]
.parse()
.unwrap_or(0),
opackets: vec_list[ProcNetDevIndexes::Opackets as usize]
.parse()
.unwrap_or(0),
};
Ok(d)
}
#[cfg(target_os = "linux")]
pub fn read_net() -> Vec<Device> {
use std::fs::File;
use std::io::{prelude::*, BufReader};
let mut devs = Vec::new();
let file = File::open("/proc/net/dev").unwrap();
let reader = BufReader::new(file);
for line in reader.lines() {
let a = line.unwrap();
let d = line_to_device_struct(&a);
match d {
Err(_x) => {}
Ok(d) => {
devs.push(d);
}
}
}
devs
}
pub type Int64T = ::std::os::raw::c_long;
pub type Uint64T = ::std::os::raw::c_ulong;
pub type UChar = ::std::os::raw::c_uchar;
pub type TimeT = Int64T;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct timespec {
pub tv_sec: TimeT,
pub tv_nsec: ::std::os::raw::c_long,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct rtnl_link_stats {
pub ifi_type: UChar,
pub ifi_addrlen: UChar,
pub ifi_hdrlen: UChar,
pub ifi_link_state: ::std::os::raw::c_int,
pub ifi_mtu: Uint64T,
pub ifi_metric: Uint64T,
pub ifi_baudrate: Uint64T,
pub ifi_ipackets: Uint64T,
pub ifi_ierrors: Uint64T,
pub ifi_opackets: Uint64T,
pub ifi_oerrors: Uint64T,
pub ifi_collisions: Uint64T,
pub ifi_ibytes: Uint64T,
pub ifi_obytes: Uint64T,
pub ifi_imcasts: Uint64T,
pub ifi_omcasts: Uint64T,
pub ifi_iqdrops: Uint64T,
pub ifi_noproto: Uint64T,
pub ifi_lastchange: timespec,
}
#[cfg(not(target_os = "linux"))]
pub fn read_net() -> Vec<Device> {
let mut devs = Vec::new();
unsafe {
let mut addrs: *mut libc::ifaddrs = std::mem::MaybeUninit::uninit().as_mut_ptr();
let _a = libc::getifaddrs(&mut addrs);
loop {
if (*(*addrs).ifa_addr).sa_family as i32 == libc::AF_LINK {
let s: *const rtnl_link_stats = ((*addrs).ifa_data) as *const rtnl_link_stats;
let d = Device {
name: ffi::CStr::from_ptr((*addrs).ifa_name)
.to_str()
.unwrap()
.to_string(),
ibytes: (*s).ifi_ibytes as i64,
obytes: (*s).ifi_obytes as i64,
ipackets: (*s).ifi_ipackets as i64,
opackets: (*s).ifi_opackets as i64,
};
devs.push(d);
}
addrs = (*addrs).ifa_next;
if std::ptr::null() == addrs {
freeifaddrs(addrs);
break;
}
}
};
devs
}
pub fn human_unit(value: i64) -> String {
let kb = 1024;
let mb = 1024 * 1024;
let gb = 1024 * 1024 * 1024;
let tb = 1024 * 1024 * 1024 * 1024;
let pb = 1024 * 1024 * 1024 * 1024 * 1024;
if value >= pb {
return format!("{} PiB", value / pb);
}
if value >= tb {
return format!("{} TiB", value / tb);
}
if value >= gb {
return format!("{} GiB", value / gb);
}
if value >= mb {
return format!("{} MiB", value / mb);
}
if value >= kb {
return format!("{} KiB", value / kb);
}
value.to_string()
}
pub fn format_output(format_str: &str, device: &Device) -> String {
format_str
.replace("\\n", "\n")
.replace("\\t", "\t")
.replace("\\r", "\r")
.replace("%{if}", &device.name)
.replace("%{ibytes}", &device.ibytes.to_string())
.replace("%{obytes}", &device.obytes.to_string())
.replace("%{ipackets}", &device.ipackets.to_string())
.replace("%{opackets}", &device.opackets.to_string())
}