ip/concrete/
interface.rs

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    /// An IP interface, consisting of am address and prefix length.
18    #[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        /// Construct a new [`Interface<A>`] from an address and prefix length.
26        pub const fn new(address: Address<A>, length: PrefixLength<A>) -> Self {
27            Self { address, length }
28        }
29
30        /// Get the address of this interface.
31        pub const fn address(&self) -> Address<A> {
32            self.address
33        }
34
35        /// Get the prefix length of this interface.
36        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}