use std::collections::HashMap;
use std::fmt;
use std::net::{AddrParseError, IpAddr};
use std::num::ParseIntError;
use std::str::FromStr;
use crate::{NetworkDataInner, NetworksInner};
pub struct Networks {
pub(crate) inner: NetworksInner,
}
impl<'a> IntoIterator for &'a Networks {
type Item = (&'a String, &'a NetworkData);
type IntoIter = std::collections::hash_map::Iter<'a, String, NetworkData>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl Default for Networks {
fn default() -> Self {
Networks::new()
}
}
impl Networks {
pub fn new() -> Self {
Self {
inner: NetworksInner::new(),
}
}
pub fn new_with_refreshed_list() -> Self {
let mut networks = Self::new();
networks.refresh(false);
networks
}
pub fn list(&self) -> &HashMap<String, NetworkData> {
self.inner.list()
}
pub fn refresh(&mut self, remove_not_listed_interfaces: bool) {
self.inner.refresh(remove_not_listed_interfaces)
}
}
impl std::ops::Deref for Networks {
type Target = HashMap<String, NetworkData>;
fn deref(&self) -> &Self::Target {
self.list()
}
}
pub struct NetworkData {
pub(crate) inner: NetworkDataInner,
}
impl NetworkData {
pub fn received(&self) -> u64 {
self.inner.received()
}
pub fn total_received(&self) -> u64 {
self.inner.total_received()
}
pub fn transmitted(&self) -> u64 {
self.inner.transmitted()
}
pub fn total_transmitted(&self) -> u64 {
self.inner.total_transmitted()
}
pub fn packets_received(&self) -> u64 {
self.inner.packets_received()
}
pub fn total_packets_received(&self) -> u64 {
self.inner.total_packets_received()
}
pub fn packets_transmitted(&self) -> u64 {
self.inner.packets_transmitted()
}
pub fn total_packets_transmitted(&self) -> u64 {
self.inner.total_packets_transmitted()
}
pub fn errors_on_received(&self) -> u64 {
self.inner.errors_on_received()
}
pub fn total_errors_on_received(&self) -> u64 {
self.inner.total_errors_on_received()
}
pub fn errors_on_transmitted(&self) -> u64 {
self.inner.errors_on_transmitted()
}
pub fn total_errors_on_transmitted(&self) -> u64 {
self.inner.total_errors_on_transmitted()
}
pub fn mac_address(&self) -> MacAddr {
self.inner.mac_address()
}
pub fn ip_networks(&self) -> &[IpNetwork] {
self.inner.ip_networks()
}
pub fn mtu(&self) -> u64 {
self.inner.mtu()
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub struct MacAddr(pub [u8; 6]);
impl MacAddr {
pub const UNSPECIFIED: Self = MacAddr([0; 6]);
pub fn is_unspecified(&self) -> bool {
self == &MacAddr::UNSPECIFIED
}
}
impl fmt::Display for MacAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let data = &self.0;
write!(
f,
"{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
data[0], data[1], data[2], data[3], data[4], data[5],
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MacAddrFromStrError {
IntError(ParseIntError),
InvalidAddrFormat,
}
impl FromStr for MacAddr {
type Err = MacAddrFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s
.split(':')
.map(|s| u8::from_str_radix(s, 16).map_err(MacAddrFromStrError::IntError));
let Some(data0) = parts.next() else {
return Err(MacAddrFromStrError::InvalidAddrFormat);
};
let Some(data1) = parts.next() else {
return Err(MacAddrFromStrError::InvalidAddrFormat);
};
let Some(data2) = parts.next() else {
return Err(MacAddrFromStrError::InvalidAddrFormat);
};
let Some(data3) = parts.next() else {
return Err(MacAddrFromStrError::InvalidAddrFormat);
};
let Some(data4) = parts.next() else {
return Err(MacAddrFromStrError::InvalidAddrFormat);
};
let Some(data5) = parts.next() else {
return Err(MacAddrFromStrError::InvalidAddrFormat);
};
if parts.next().is_some() {
return Err(MacAddrFromStrError::InvalidAddrFormat);
}
Ok(MacAddr([data0?, data1?, data2?, data3?, data4?, data5?]))
}
}
impl core::fmt::Display for MacAddrFromStrError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::IntError(error) => write!(f, "a number is not in hexadecimal format: {error}"),
Self::InvalidAddrFormat => f.write_str("invalid address format"),
}
}
}
impl core::error::Error for MacAddrFromStrError {}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub struct IpNetwork {
pub addr: IpAddr,
pub prefix: u8,
}
impl fmt::Display for IpNetwork {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.addr, self.prefix)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IpNetworkFromStrError {
PrefixError(ParseIntError),
AddrParseError(AddrParseError),
InvalidAddrFormat,
}
impl FromStr for IpNetwork {
type Err = IpNetworkFromStrError;
#[allow(clippy::from_str_radix_10)]
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split('/');
let Some(addr) = parts.next() else {
return Err(IpNetworkFromStrError::InvalidAddrFormat);
};
let Some(prefix) = parts.next() else {
return Err(IpNetworkFromStrError::InvalidAddrFormat);
};
if parts.next().is_some() {
return Err(IpNetworkFromStrError::InvalidAddrFormat);
}
Ok(IpNetwork {
addr: IpAddr::from_str(addr).map_err(IpNetworkFromStrError::AddrParseError)?,
prefix: u8::from_str_radix(prefix, 10).map_err(IpNetworkFromStrError::PrefixError)?,
})
}
}
impl core::fmt::Display for IpNetworkFromStrError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::PrefixError(error) => write!(f, "prefix is not an integer: {error}"),
Self::AddrParseError(error) => write!(f, "failed to parse IP address: {error}"),
Self::InvalidAddrFormat => {
f.write_str("invalid address format, should `[IP adress]/[number]`")
}
}
}
}
impl core::error::Error for IpNetworkFromStrError {}
#[cfg(test)]
mod tests {
use crate::*;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
#[test]
fn check_display_impl_mac_address() {
println!(
"{} {:?}",
MacAddr([0x1, 0x2, 0x3, 0x4, 0x5, 0x6]),
MacAddr([0xa, 0xb, 0xc, 0xd, 0xe, 0xf])
);
}
#[test]
fn check_mac_address_is_unspecified_true() {
assert!(MacAddr::UNSPECIFIED.is_unspecified());
assert!(MacAddr([0; 6]).is_unspecified());
}
#[test]
fn check_mac_address_is_unspecified_false() {
assert!(!MacAddr([1, 2, 3, 4, 5, 6]).is_unspecified());
}
#[test]
fn check_mac_address_conversions() {
let mac = MacAddr([0xa, 0xb, 0xc, 0xd, 0xe, 0xf]);
let mac_s = mac.to_string();
assert_eq!("0a:0b:0c:0d:0e:0f", mac_s);
assert_eq!(Ok(mac), MacAddr::from_str(&mac_s));
assert_eq!(
MacAddr::from_str("0a:0b:0c:0d:0e:0f:01"),
Err(MacAddrFromStrError::InvalidAddrFormat)
);
assert_eq!(
MacAddr::from_str("0a:0b:0c:0d:0e"),
Err(MacAddrFromStrError::InvalidAddrFormat)
);
}
#[test]
fn check_display_impl_ip_network_ipv4() {
println!(
"{} {:?}",
IpNetwork {
addr: IpAddr::from(Ipv4Addr::new(1, 2, 3, 4)),
prefix: 3
},
IpNetwork {
addr: IpAddr::from(Ipv4Addr::new(255, 255, 255, 0)),
prefix: 21
}
);
}
#[test]
fn check_display_impl_ip_network_ipv6() {
println!(
"{} {:?}",
IpNetwork {
addr: IpAddr::from(Ipv6Addr::new(0xffff, 0xaabb, 00, 0, 0, 0x000c, 11, 21)),
prefix: 127
},
IpNetwork {
addr: IpAddr::from(Ipv6Addr::new(0xffcc, 0, 0, 0xffcc, 0, 0xffff, 0, 0xccaa)),
prefix: 120
}
)
}
#[test]
fn check_ip_networks() {
if !IS_SUPPORTED_SYSTEM {
return;
}
let networks = Networks::new_with_refreshed_list();
if networks.iter().any(|(_, n)| !n.ip_networks().is_empty()) {
return;
}
panic!("Networks should have at least one IP network ");
}
#[test]
fn check_ip_network_conversions() {
let addr = IpNetwork {
addr: IpAddr::from(Ipv6Addr::new(0xff, 0xa, 0x8, 0x12, 0x7, 0xc, 0xa, 0xb)),
prefix: 12,
};
let addr_s = addr.to_string();
assert_eq!("ff:a:8:12:7:c:a:b/12", addr_s);
assert_eq!(Ok(addr), IpNetwork::from_str(&addr_s));
let addr = IpNetwork {
addr: IpAddr::from(Ipv4Addr::new(255, 255, 255, 0)),
prefix: 21,
};
let addr_s = addr.to_string();
assert_eq!("255.255.255.0/21", addr_s);
assert_eq!(Ok(addr), IpNetwork::from_str(&addr_s));
assert_eq!(
IpNetwork::from_str("ff:a:8:12:7:c:a:b"),
Err(IpNetworkFromStrError::InvalidAddrFormat)
);
assert_eq!(
IpNetwork::from_str("ff:a:8:12:7:c:a:b/12/12"),
Err(IpNetworkFromStrError::InvalidAddrFormat)
);
match IpNetwork::from_str("0a:0b:0c:0d:0e/12") {
Err(IpNetworkFromStrError::AddrParseError(_)) => {}
x => panic!("expected `IpNetworkFromStrError::AddrParseError`, found {x:?}"),
}
}
}