ip/concrete/prefix/
len.rs1use core::ops::Neg;
2use core::{fmt, str::FromStr};
3
4use super::impl_try_from_any;
5use crate::{
6 any,
7 error::{err, Error, Kind},
8 traits::{
9 self,
10 primitive::{self, Address as _, Length as _},
11 Afi,
12 },
13 Ipv4, Ipv6,
14};
15
16#[allow(clippy::wildcard_imports)]
17mod private {
18 use super::*;
19
20 #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
23 pub struct PrefixLength<A: Afi>(<A::Primitive as primitive::Address<A>>::Length);
24
25 impl<A: Afi> PrefixLength<A> {
26 pub const MIN: Self = Self(A::Primitive::MIN_LENGTH);
28
29 pub const MAX: Self = Self(A::Primitive::MAX_LENGTH);
31
32 pub fn from_primitive(
40 n: <A::Primitive as primitive::Address<A>>::Length,
41 ) -> Result<Self, Error> {
42 if A::Primitive::MIN_LENGTH <= n && n <= A::Primitive::MAX_LENGTH {
43 Ok(Self(n))
44 } else {
45 Err(err!(Kind::PrefixLength))
46 }
47 }
48
49 pub const fn into_primitive(self) -> <A::Primitive as primitive::Address<A>>::Length {
51 self.0
52 }
53 }
54
55 impl<A> PrefixLength<A>
56 where
57 A: Afi,
58 A::Primitive: primitive::Address<A, Length = u8>,
59 {
60 pub(super) const fn as_u8(&self) -> &u8 {
61 &self.0
62 }
63 }
64}
65
66pub use self::private::PrefixLength;
67
68impl<A: Afi> TryFrom<usize> for PrefixLength<A> {
69 type Error = Error;
70
71 fn try_from(value: usize) -> Result<Self, Self::Error> {
72 value
73 .try_into()
74 .map_err(|_| err!(Kind::PrefixLength))
75 .and_then(Self::from_primitive)
76 }
77}
78
79impl<A: Afi> traits::PrefixLength for PrefixLength<A> {
80 fn increment(self) -> Result<Self, Error> {
81 let l = self.into_primitive();
82 if l < <A::Primitive as primitive::Address<A>>::MAX_LENGTH {
83 Self::from_primitive(l + <A::Primitive as primitive::Address<A>>::Length::ONE)
84 } else {
85 Err(err!(Kind::PrefixLength))
86 }
87 }
88 fn decrement(self) -> Result<Self, Error> {
89 let l = self.into_primitive();
90 if l > <A::Primitive as primitive::Address<A>>::Length::ZERO {
91 Self::from_primitive(l - <A::Primitive as primitive::Address<A>>::Length::ONE)
92 } else {
93 Err(err!(Kind::PrefixLength))
94 }
95 }
96}
97
98impl<A: Afi> FromStr for PrefixLength<A> {
99 type Err = Error;
100
101 fn from_str(s: &str) -> Result<Self, Self::Err> {
102 A::Primitive::parse_length(s).and_then(Self::from_primitive)
103 }
104}
105
106impl<A: Afi> AsRef<u8> for PrefixLength<A>
107where
108 A::Primitive: primitive::Address<A, Length = u8>,
109{
110 fn as_ref(&self) -> &u8 {
111 self.as_u8()
112 }
113}
114
115impl_try_from_any! {
116 any::PrefixLength {
117 any::PrefixLength::Ipv4 => PrefixLength<Ipv4>,
118 any::PrefixLength::Ipv6 => PrefixLength<Ipv6>,
119 }
120}
121
122impl<A: Afi> fmt::Display for PrefixLength<A> {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 self.into_primitive().fmt(f)
125 }
126}
127
128impl<A: Afi> Neg for PrefixLength<A> {
129 type Output = Self;
130
131 fn neg(self) -> Self::Output {
132 Self::from_primitive(A::Primitive::MAX_LENGTH - self.into_primitive()).unwrap()
134 }
135}
136
137#[cfg(any(test, feature = "arbitrary"))]
138use proptest::{
139 arbitrary::Arbitrary,
140 strategy::{BoxedStrategy, Strategy},
141};
142
143#[cfg(any(test, feature = "arbitrary"))]
144impl<A: Afi> Arbitrary for PrefixLength<A>
145where
146 <A::Primitive as primitive::Address<A>>::Length: 'static,
147 core::ops::RangeInclusive<<A::Primitive as primitive::Address<A>>::Length>:
148 Strategy<Value = <A::Primitive as primitive::Address<A>>::Length>,
149{
150 type Parameters = ();
151 type Strategy = BoxedStrategy<Self>;
152 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
153 (A::Primitive::MIN_LENGTH..=A::Primitive::MAX_LENGTH)
154 .prop_map(|l| Self::from_primitive(l).unwrap())
155 .boxed()
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 mod ipv4 {
164 use super::*;
165
166 #[test]
167 fn parse_valid() {
168 let input = "/8";
169 let length = input.parse::<PrefixLength<Ipv4>>().unwrap();
170 assert_eq!(length.as_ref(), &8);
171 }
172
173 #[test]
174 fn parse_invalid() {
175 let input = "/40";
176 let length = input.parse::<PrefixLength<Ipv4>>();
177 assert!(length.is_err_and(|err| err.kind() == Kind::PrefixLength));
178 }
179 }
180
181 mod ipv6 {
182 use super::*;
183
184 #[test]
185 fn parse_valid() {
186 let input = "/40";
187 let length = input.parse::<PrefixLength<Ipv6>>().unwrap();
188 assert_eq!(length.as_ref(), &40);
189 }
190
191 #[test]
192 fn parse_invalid() {
193 let input = "128";
194 let length = input.parse::<PrefixLength<Ipv6>>();
195 assert!(length.is_err_and(|err| err.kind() == Kind::ParserError));
196 }
197 }
198}