use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
macro_rules! if_addr {
($kind:literal) => {
paste::paste! {
#[doc = "An interface IP" $kind " address."]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct [<If $kind Addr>] {
index: u32,
addr: [<Ip $kind Addr>],
}
impl core::fmt::Display for [<If $kind Addr>] {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{} ({})", self.addr, self.index)
}
}
impl core::ops::Deref for [<If $kind Addr>] {
type Target = [<Ip $kind Addr>];
#[inline]
fn deref(&self) -> &Self::Target {
&self.addr
}
}
impl [<If $kind Addr>] {
#[doc = "Creates a new `If" $kind "Addr` from an [`Ip" $kind "Addr`]."]
#[inline]
pub const fn new(index: u32, addr: [<Ip $kind Addr>]) -> Self {
Self {
index,
addr,
}
}
#[inline]
pub const fn index(&self) -> u32 {
self.index
}
pub fn name(&self) -> std::io::Result<smol_str::SmolStr> {
crate::idx_to_name::ifindex_to_name(self.index)
}
#[inline]
pub const fn addr(&self) -> [<Ip $kind Addr>] {
self.addr
}
}
}
};
}
if_addr!("v4");
if_addr!("v6");
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum IfAddr {
V4(Ifv4Addr),
V6(Ifv6Addr),
}
impl core::fmt::Display for IfAddr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::V4(addr) => write!(f, "{addr}"),
Self::V6(addr) => write!(f, "{addr}"),
}
}
}
impl From<Ifv4Addr> for IfAddr {
fn from(value: Ifv4Addr) -> Self {
Self::V4(value)
}
}
impl From<Ifv6Addr> for IfAddr {
fn from(value: Ifv6Addr) -> Self {
Self::V6(value)
}
}
impl IfAddr {
#[inline]
pub const fn new(index: u32, addr: IpAddr) -> Self {
match addr {
IpAddr::V4(addr) => Self::V4(Ifv4Addr::new(index, addr)),
IpAddr::V6(addr) => Self::V6(Ifv6Addr::new(index, addr)),
}
}
#[inline]
pub const fn index(&self) -> u32 {
match self {
Self::V4(addr) => addr.index(),
Self::V6(addr) => addr.index(),
}
}
pub fn name(&self) -> std::io::Result<smol_str::SmolStr> {
crate::idx_to_name::ifindex_to_name(self.index())
}
#[inline]
pub const fn addr(&self) -> IpAddr {
match self {
Self::V4(addr) => IpAddr::V4(addr.addr()),
Self::V6(addr) => IpAddr::V6(addr.addr()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ifv4addr() {
let addr = Ifv4Addr::new(1, Ipv4Addr::new(127, 0, 0, 1));
assert_eq!(addr.index(), 1);
assert_eq!(addr.addr(), Ipv4Addr::new(127, 0, 0, 1));
assert!(addr.name().is_ok());
let _ = addr.is_private();
}
#[test]
fn ifv6addr() {
let addr = Ifv6Addr::new(1, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
assert_eq!(addr.index(), 1);
assert_eq!(addr.addr(), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
assert!(addr.name().is_ok());
let _ = addr.is_loopback();
}
#[test]
fn ifaddr() {
let addr = IfAddr::new(1, IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
assert_eq!(addr.index(), 1);
assert_eq!(addr.addr(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
assert!(addr.name().is_ok());
println!("{addr}");
let addr = IfAddr::new(1, IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
assert_eq!(addr.index(), 1);
assert_eq!(
addr.addr(),
IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))
);
assert!(addr.name().is_ok());
println!("{addr}");
}
}