1use core::{marker, fmt};
4use core::net::{IpAddr, SocketAddr};
5
6pub trait Filter: Sized {
10 fn is_match(&self, ip: IpAddr) -> bool;
12 #[inline(always)]
13 fn or<F2: Filter>(self, right: F2) -> Or<Self, F2> {
15 or(self, right)
16 }
17}
18
19impl Filter for () {
20 #[inline(always)]
21 fn is_match(&self, _: IpAddr) -> bool {
23 false
24 }
25}
26
27impl Filter for IpAddr {
28 #[inline(always)]
29 fn is_match(&self, ip: IpAddr) -> bool {
30 *self == ip
31 }
32}
33
34impl Filter for SocketAddr {
35 #[inline(always)]
36 fn is_match(&self, ip: IpAddr) -> bool {
37 self.ip() == ip
38 }
39}
40
41pub struct Or<F1, F2> {
43 left: F1,
44 right: F2,
45}
46
47impl<F1: Filter, F2: Filter> Filter for Or<F1, F2> {
48 #[inline(always)]
49 fn is_match(&self, ip: IpAddr) -> bool {
50 self.left.is_match(ip) || self.right.is_match(ip)
51 }
52}
53
54pub struct CollectionOr<I, F> {
58 collection: I,
59 _filter: marker::PhantomData<F>,
60}
61
62impl<F: Filter, I: AsRef<[F]>> CollectionOr<I, F> {
63 #[inline(always)]
64 pub const fn new(collection: I) -> Self {
66 Self {
67 collection,
68 _filter: marker::PhantomData
69 }
70 }
71}
72
73impl<F: Filter, I: AsRef<[F]>> Filter for CollectionOr<I, F> {
74 #[inline(always)]
75 fn is_match(&self, ip: IpAddr) -> bool {
76 self.collection.as_ref().iter().any(|filter| filter.is_match(ip))
77 }
78}
79
80#[derive(Debug, PartialEq, Eq)]
81enum ParseError<'a> {
83 ParseError(ip_cidr::ParseError<'a>),
85 InvalidPrefix
87}
88
89#[repr(transparent)]
90#[derive(PartialEq, Eq)]
91pub struct CidrParseError<'a>(ParseError<'a>);
93
94impl fmt::Debug for CidrParseError<'_> {
95 #[inline(always)]
96 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
97 fmt::Debug::fmt(&self.0, fmt)
98 }
99}
100
101impl fmt::Display for CidrParseError<'_> {
102 #[inline(always)]
103 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
104 match &self.0 {
105 ParseError::InvalidPrefix => fmt.write_str("Invalid CIDR prefix"),
106 ParseError::ParseError(error) => fmt::Display::fmt(error, fmt),
107 }
108 }
109}
110
111#[repr(transparent)]
112#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
113pub struct Cidr(ip_cidr::Cidr);
115
116impl Cidr {
117 #[inline]
118 pub const fn from_text(text: &str) -> Result<Self, CidrParseError<'_>> {
120 match ip_cidr::parse_cidr(text) {
121 Ok(Some(inner)) => Ok(Self(inner)),
122 Ok(None) => Err(CidrParseError(ParseError::InvalidPrefix)),
123 Err(error) => Err(CidrParseError(ParseError::ParseError(error))),
124 }
125 }
126
127 #[inline]
128 pub const fn new(ip: IpAddr, prefix: u8) -> Result<Self, CidrParseError<'static>> {
130 match ip_cidr::Cidr::new(ip, prefix) {
131 Some(cidr) => Ok(Self(cidr)),
132 None => Err(CidrParseError(ParseError::InvalidPrefix)),
133 }
134 }
135}
136
137impl Filter for Cidr {
138 #[inline(always)]
139 fn is_match(&self, ip: IpAddr) -> bool {
140 self.0.contains(ip)
141 }
142}
143
144impl fmt::Debug for Cidr {
145 #[inline(always)]
146 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
147 fmt::Debug::fmt(&self.0, fmt)
148 }
149}
150
151impl fmt::Display for Cidr {
152 #[inline(always)]
153 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
154 fmt::Display::fmt(&self.0, fmt)
155 }
156}
157
158#[inline]
159pub const fn or<F1, F2>(left: F1, right: F2) -> Or<F1, F2> {
161 Or {
162 left,
163 right
164 }
165}
166
167#[inline]
168pub const fn collection_or<F: Filter, I: AsRef<[F]>>(collection: I) -> CollectionOr<I, F> {
170 CollectionOr::new(collection)
171}