1use core::fmt;
2use core::str::FromStr;
3
4use super::{impl_try_from_any, Address, Prefix, PrefixLength};
5use crate::{
6 any,
7 error::Error,
8 fmt::AddressDisplay,
9 traits::{self, primitive::Address as _, Afi, Prefix as _},
10 Ipv4, Ipv6,
11};
12
13#[allow(clippy::wildcard_imports)]
14mod private {
15 use super::*;
16
17 #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
19 pub struct Interface<A: Afi> {
20 address: Address<A>,
21 length: PrefixLength<A>,
22 }
23
24 impl<A: Afi> Interface<A> {
25 pub const fn new(address: Address<A>, length: PrefixLength<A>) -> Self {
27 Self { address, length }
28 }
29
30 pub const fn address(&self) -> Address<A> {
32 self.address
33 }
34
35 pub const fn length(&self) -> PrefixLength<A> {
37 self.length
38 }
39 }
40}
41
42pub use self::private::Interface;
43
44impl<A: Afi> traits::Interface for Interface<A> {
45 type Address = Address<A>;
46 type Prefix = Prefix<A>;
47 type PrefixLength = PrefixLength<A>;
48
49 fn network(&self) -> Self::Address {
50 self.trunc().network()
51 }
52
53 fn addr(&self) -> Self::Address {
54 self.address()
55 }
56
57 fn trunc(&self) -> Self::Prefix {
58 Self::Prefix::from(*self)
59 }
60
61 fn prefix_len(&self) -> Self::PrefixLength {
62 self.length()
63 }
64
65 fn broadcast(&self) -> Self::Address {
66 let prefix = self.trunc();
67 prefix.network() | prefix.hostmask()
68 }
69}
70
71impl<A: Afi> From<Address<A>> for Interface<A> {
72 fn from(addr: Address<A>) -> Self {
73 Self::new(addr, PrefixLength::MAX)
74 }
75}
76
77impl<A: Afi> FromStr for Interface<A> {
78 type Err = Error;
79
80 fn from_str(s: &str) -> Result<Self, Self::Err> {
81 A::Primitive::parse_prefix(s).and_then(|(addr, len)| {
82 Ok(Self::new(
83 Address::new(addr),
84 PrefixLength::from_primitive(len)?,
85 ))
86 })
87 }
88}
89
90impl_try_from_any! {
91 any::Interface {
92 any::Interface::Ipv4 => Interface<Ipv4>,
93 any::Interface::Ipv6 => Interface<Ipv6>,
94 }
95}
96
97impl<A: Afi> fmt::Display for Interface<A>
98where
99 A::Primitive: AddressDisplay<A>,
100{
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 write!(f, "{}/{}", self.address(), self.length())
103 }
104}
105
106#[cfg(feature = "ipnet")]
107impl From<ipnet::Ipv4Net> for Interface<Ipv4> {
108 fn from(net: ipnet::Ipv4Net) -> Self {
109 let address = net.addr().into();
110 let length = PrefixLength::from_primitive(net.prefix_len())
111 .expect("we trusted `ipnet` to enforce length bounds");
112 Self::new(address, length)
113 }
114}
115
116#[cfg(feature = "ipnet")]
117impl From<ipnet::Ipv6Net> for Interface<Ipv6> {
118 fn from(net: ipnet::Ipv6Net) -> Self {
119 let address = net.addr().into();
120 let length = PrefixLength::from_primitive(net.prefix_len())
121 .expect("we trusted `ipnet` to enforce length bounds");
122 Self::new(address, length)
123 }
124}
125
126#[cfg(any(test, feature = "arbitrary"))]
127use proptest::{
128 arbitrary::{any_with, Arbitrary, ParamsFor, StrategyFor},
129 strategy::{BoxedStrategy, Strategy},
130};
131
132#[cfg(any(test, feature = "arbitrary"))]
133impl<A: Afi> Arbitrary for Interface<A>
134where
135 Address<A>: Arbitrary,
136 StrategyFor<Address<A>>: 'static,
137 PrefixLength<A>: Arbitrary,
138 StrategyFor<PrefixLength<A>>: 'static,
139{
140 type Parameters = ParamsFor<(Address<A>, PrefixLength<A>)>;
141 type Strategy = BoxedStrategy<Self>;
142
143 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
144 (
145 any_with::<Address<A>>(params.0),
146 any_with::<PrefixLength<A>>(params.1),
147 )
148 .prop_map(|(address, length)| Self::new(address, length))
149 .boxed()
150 }
151}