use core::{marker, fmt};
use core::net::{IpAddr, SocketAddr};
pub trait Filter: Sized {
fn is_match(&self, ip: IpAddr) -> bool;
#[inline(always)]
fn or<F2: Filter>(self, right: F2) -> Or<Self, F2> {
or(self, right)
}
}
impl Filter for () {
#[inline(always)]
fn is_match(&self, _: IpAddr) -> bool {
false
}
}
impl Filter for IpAddr {
#[inline(always)]
fn is_match(&self, ip: IpAddr) -> bool {
*self == ip
}
}
impl Filter for SocketAddr {
#[inline(always)]
fn is_match(&self, ip: IpAddr) -> bool {
self.ip() == ip
}
}
pub struct Or<F1, F2> {
left: F1,
right: F2,
}
impl<F1: Filter, F2: Filter> Filter for Or<F1, F2> {
#[inline(always)]
fn is_match(&self, ip: IpAddr) -> bool {
self.left.is_match(ip) || self.right.is_match(ip)
}
}
pub struct CollectionOr<I, F> {
collection: I,
_filter: marker::PhantomData<F>,
}
impl<F: Filter, I: AsRef<[F]>> CollectionOr<I, F> {
#[inline(always)]
pub const fn new(collection: I) -> Self {
Self {
collection,
_filter: marker::PhantomData
}
}
}
impl<F: Filter, I: AsRef<[F]>> Filter for CollectionOr<I, F> {
#[inline(always)]
fn is_match(&self, ip: IpAddr) -> bool {
self.collection.as_ref().iter().any(|filter| filter.is_match(ip))
}
}
#[derive(Debug, PartialEq, Eq)]
enum ParseError<'a> {
ParseError(ip_cidr::ParseError<'a>),
InvalidPrefix
}
#[repr(transparent)]
#[derive(PartialEq, Eq)]
pub struct CidrParseError<'a>(ParseError<'a>);
impl fmt::Debug for CidrParseError<'_> {
#[inline(always)]
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, fmt)
}
}
impl fmt::Display for CidrParseError<'_> {
#[inline(always)]
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
ParseError::InvalidPrefix => fmt.write_str("Invalid CIDR prefix"),
ParseError::ParseError(error) => fmt::Display::fmt(error, fmt),
}
}
}
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Cidr(ip_cidr::Cidr);
impl Cidr {
#[inline]
pub const fn from_text(text: &str) -> Result<Self, CidrParseError<'_>> {
match ip_cidr::parse_cidr(text) {
Ok(Some(inner)) => Ok(Self(inner)),
Ok(None) => Err(CidrParseError(ParseError::InvalidPrefix)),
Err(error) => Err(CidrParseError(ParseError::ParseError(error))),
}
}
#[inline]
pub const fn new(ip: IpAddr, prefix: u8) -> Result<Self, CidrParseError<'static>> {
match ip_cidr::Cidr::new(ip, prefix) {
Some(cidr) => Ok(Self(cidr)),
None => Err(CidrParseError(ParseError::InvalidPrefix)),
}
}
}
impl Filter for Cidr {
#[inline(always)]
fn is_match(&self, ip: IpAddr) -> bool {
self.0.contains(ip)
}
}
impl fmt::Debug for Cidr {
#[inline(always)]
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, fmt)
}
}
impl fmt::Display for Cidr {
#[inline(always)]
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, fmt)
}
}
#[inline]
pub const fn or<F1, F2>(left: F1, right: F2) -> Or<F1, F2> {
Or {
left,
right
}
}
#[inline]
pub const fn collection_or<F: Filter, I: AsRef<[F]>>(collection: I) -> CollectionOr<I, F> {
CollectionOr::new(collection)
}