use core::fmt;
use core::str::FromStr;
use super::{impl_try_from_any, Address, Prefix, PrefixLength};
use crate::{
any,
error::Error,
fmt::AddressDisplay,
traits::{self, primitive::Address as _, Afi, Prefix as _},
Ipv4, Ipv6,
};
#[allow(clippy::wildcard_imports)]
mod private {
use super::*;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct Interface<A: Afi> {
address: Address<A>,
length: PrefixLength<A>,
}
impl<A: Afi> Interface<A> {
pub const fn new(address: Address<A>, length: PrefixLength<A>) -> Self {
Self { address, length }
}
pub const fn address(&self) -> Address<A> {
self.address
}
pub const fn length(&self) -> PrefixLength<A> {
self.length
}
}
}
pub use self::private::Interface;
impl<A: Afi> traits::Interface for Interface<A> {
type Address = Address<A>;
type Prefix = Prefix<A>;
type PrefixLength = PrefixLength<A>;
fn network(&self) -> Self::Address {
self.trunc().network()
}
fn addr(&self) -> Self::Address {
self.address()
}
fn trunc(&self) -> Self::Prefix {
Self::Prefix::from(*self)
}
fn prefix_len(&self) -> Self::PrefixLength {
self.length()
}
fn broadcast(&self) -> Self::Address {
let prefix = self.trunc();
prefix.network() | prefix.hostmask()
}
}
impl<A: Afi> From<Address<A>> for Interface<A> {
fn from(addr: Address<A>) -> Self {
Self::new(addr, PrefixLength::MAX)
}
}
impl<A: Afi> FromStr for Interface<A> {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
A::Primitive::parse_prefix(s).and_then(|(addr, len)| {
Ok(Self::new(
Address::new(addr),
PrefixLength::from_primitive(len)?,
))
})
}
}
impl_try_from_any! {
any::Interface {
any::Interface::Ipv4 => Interface<Ipv4>,
any::Interface::Ipv6 => Interface<Ipv6>,
}
}
impl<A: Afi> fmt::Display for Interface<A>
where
A::Primitive: AddressDisplay<A>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.address(), self.length())
}
}
#[cfg(feature = "ipnet")]
impl From<ipnet::Ipv4Net> for Interface<Ipv4> {
fn from(net: ipnet::Ipv4Net) -> Self {
let address = net.addr().into();
let length = PrefixLength::from_primitive(net.prefix_len())
.expect("we trusted `ipnet` to enforce length bounds");
Self::new(address, length)
}
}
#[cfg(feature = "ipnet")]
impl From<ipnet::Ipv6Net> for Interface<Ipv6> {
fn from(net: ipnet::Ipv6Net) -> Self {
let address = net.addr().into();
let length = PrefixLength::from_primitive(net.prefix_len())
.expect("we trusted `ipnet` to enforce length bounds");
Self::new(address, length)
}
}
#[cfg(any(test, feature = "arbitrary"))]
use proptest::{
arbitrary::{any_with, Arbitrary, ParamsFor, StrategyFor},
strategy::{BoxedStrategy, Strategy},
};
#[cfg(any(test, feature = "arbitrary"))]
impl<A: Afi> Arbitrary for Interface<A>
where
Address<A>: Arbitrary,
StrategyFor<Address<A>>: 'static,
PrefixLength<A>: Arbitrary,
StrategyFor<PrefixLength<A>>: 'static,
{
type Parameters = ParamsFor<(Address<A>, PrefixLength<A>)>;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
(
any_with::<Address<A>>(params.0),
any_with::<PrefixLength<A>>(params.1),
)
.prop_map(|(address, length)| Self::new(address, length))
.boxed()
}
}