Skip to content

Commit

Permalink
add many inet/cidr parser combinators to allow custom address parsers…
Browse files Browse the repository at this point in the history
… and ignoring host bits
  • Loading branch information
stbuehler committed Jun 24, 2024
1 parent d90ca5c commit 0784668
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 25 deletions.
8 changes: 2 additions & 6 deletions src/cidr/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use core::{
};
use std::net::IpAddr;

use super::from_str::cidr_from_str;
use crate::{
errors::*,
Family,
Expand Down Expand Up @@ -244,11 +243,8 @@ impl FromStr for AnyIpCidr {
type Err = NetworkParseError;

fn from_str(s: &str) -> Result<Self, NetworkParseError> {
if s == "any" {
Ok(Self::Any)
} else {
cidr_from_str::<IpCidr>(s).map(Self::from)
}
// TODO: use strict FromStr::from_str address parsing with version bump
crate::parsers::parse_any_cidr(s, crate::local_addr_parser::ParseableAddress::address_from_str)
}
}

Expand Down
11 changes: 2 additions & 9 deletions src/cidr/from_str.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use core::str::FromStr;

use crate::{
errors::*,
local_addr_parser::ParseableAddress,
Expand All @@ -11,11 +9,6 @@ where
C: Cidr,
C::Address: ParseableAddress,
{
match s.rfind('/') {
None => Ok(C::new_host(C::Address::address_from_str(s)?)),
Some(pos) => C::new(
C::Address::address_from_str(&s[0..pos])?,
u8::from_str(&s[pos + 1..])?,
),
}
// TODO: use strict FromStr::from_str address parsing with version bump
crate::parsers::parse_cidr(s, C::Address::address_from_str)
}
11 changes: 2 additions & 9 deletions src/inet/from_str.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use core::str::FromStr;

use crate::{
errors::NetworkParseError,
local_addr_parser::ParseableAddress,
Expand All @@ -11,11 +9,6 @@ where
I: Inet,
I::Address: ParseableAddress,
{
Ok(match s.rfind('/') {
None => I::new_host(I::Address::address_from_str(s)?),
Some(pos) => I::new(
I::Address::address_from_str(&s[0..pos])?,
u8::from_str(&s[pos + 1..])?,
)?,
})
// TODO: use strict FromStr::from_str address parsing with version bump
crate::parsers::parse_inet(s, I::Address::address_from_str)
}
172 changes: 172 additions & 0 deletions src/parsers/combinators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
use std::net::{AddrParseError, IpAddr};

use crate::{errors::NetworkParseError, Address, AnyIpCidr, Cidr, Inet, IpInet};

/// Parse [`Cidr`] with custom address and network (when no '/' separator was found) parser
///
/// If a '/' is found, parse trailing number as prefix length and leading address with `address_parser`.
/// Otherwise parse with `host_parser`.
pub fn parse_cidr_full<C, AP, NP>(s: &str, address_parser: AP, host_parser: NP) -> Result<C, NetworkParseError>
where
C: Cidr,
AP: FnOnce(&str) -> Result<C::Address, AddrParseError>,
NP: FnOnce(&str) -> Result<C, NetworkParseError>,
{
match s.rfind('/') {
None => host_parser(s),
Some(pos) => C::new(
address_parser(&s[0..pos])?,
s[pos + 1..].parse()?,
),
}
}

/// Parse [`Cidr`] with custom address parser
///
/// If a '/' is found, parse trailing number as prefix length and leading address with `address_parser`.
/// Otherwise parse `address_parser` and treat as host (maximum prefix length).
pub fn parse_cidr<C, AP>(s: &str, address_parser: AP) -> Result<C, NetworkParseError>
where
C: Cidr,
AP: Fn(&str) -> Result<C::Address, AddrParseError>,
{
parse_cidr_full(s, &address_parser, |s| {
Ok(C::new_host(address_parser(s)?))
})
}

/// Parse [`Cidr`] with custom address and network (when no '/' separator was found) parser
///
/// Similar to [`parse_cidr_full`] but ignores host bits in addresses.
pub fn parse_cidr_full_ignore_hostbits<C, AP, NP>(s: &str, address_parser: AP, host_parser: NP) -> Result<C, NetworkParseError>
where
C: Cidr,
AP: FnOnce(&str) -> Result<C::Address, AddrParseError>,
NP: FnOnce(&str) -> Result<C, NetworkParseError>,
{
match s.rfind('/') {
None => host_parser(s),
Some(pos) => {
let inet = <C::Address as Address>::Inet::new(
address_parser(&s[0..pos])?,
s[pos + 1..].parse()?,
)?;
Ok(inet.network())
},
}
}

/// Parse [`Cidr`] with custom address parser
///
/// Similar to [`parse_cidr`] but ignores host bits in addresses.
pub fn parse_cidr_ignore_hostbits<C, AP>(s: &str, address_parser: AP) -> Result<C, NetworkParseError>
where
C: Cidr,
AP: Fn(&str) -> Result<C::Address, AddrParseError>,
{
parse_cidr_full_ignore_hostbits(s, &address_parser, |s| {
Ok(C::new_host(address_parser(s)?))
})
}

/// Parse [`AnyIpCidr`] with custom address and network (when no '/' separator was found) parser
///
/// Similar to [`parse_any_cidr_full`] but ignores host bits in addresses.
pub fn parse_any_cidr_full_ignore_hostbits<AP, NP>(s: &str, address_parser: AP, host_parser: NP) -> Result<AnyIpCidr, NetworkParseError>
where
AP: FnOnce(&str) -> Result<IpAddr, AddrParseError>,
NP: FnOnce(&str) -> Result<AnyIpCidr, NetworkParseError>,
{
match s.rfind('/') {
None => host_parser(s),
Some(pos) => Ok(IpInet::new(
address_parser(&s[0..pos])?,
s[pos + 1..].parse()?,
)?.network().into()),
}
}

/// Parse [`AnyIpCidr`] with custom address parser
///
/// Similar to [`parse_any_cidr`] but ignores host bits in addresses.
pub fn parse_any_cidr_ignore_hostbits<AP>(s: &str, address_parser: AP) -> Result<AnyIpCidr, NetworkParseError>
where
AP: Fn(&str) -> Result<IpAddr, AddrParseError>,
{
parse_any_cidr_full(s, &address_parser, |s| {
if s == "any" {
Ok(AnyIpCidr::Any)
} else {
Ok(AnyIpCidr::new_host(address_parser(s)?))
}
})
}

/// Parse [`AnyIpCidr`] with custom address and network (when no '/' separator was found) parser
///
/// If a '/' is found, parse trailing number as prefix length and leading address with `address_parser`.
/// Otherwise parse with `host_parser`.
pub fn parse_any_cidr_full<AP, NP>(s: &str, address_parser: AP, host_parser: NP) -> Result<AnyIpCidr, NetworkParseError>
where
AP: FnOnce(&str) -> Result<IpAddr, AddrParseError>,
NP: FnOnce(&str) -> Result<AnyIpCidr, NetworkParseError>,
{
match s.rfind('/') {
None => host_parser(s),
Some(pos) => AnyIpCidr::new(
address_parser(&s[0..pos])?,
s[pos + 1..].parse()?,
),
}
}

/// Parse [`AnyIpCidr`] with custom address parser
///
/// If a '/' is found, parse trailing number as prefix length and leading address with `address_parser`.
/// If input is just `"any"` returns [`AnyIpCidr::Any`].
/// Otherwise parse `address_parser` and treat as host (maximum prefix length).
pub fn parse_any_cidr<AP>(s: &str, address_parser: AP) -> Result<AnyIpCidr, NetworkParseError>
where
AP: Fn(&str) -> Result<IpAddr, AddrParseError>,
{
parse_any_cidr_full(s, &address_parser, |s| {
if s == "any" {
Ok(AnyIpCidr::Any)
} else {
Ok(AnyIpCidr::new_host(address_parser(s)?))
}
})
}

/// Parse [`Inet`] with custom address and network (when no '/' separator was found) parser
///
/// If a '/' is found, parse trailing number as prefix length and leading address with `address_parser`.
/// Otherwise parse with `host_parser`.
pub fn parse_inet_full<I, AP, NP>(s: &str, address_parser: AP, host_parser: NP) -> Result<I, NetworkParseError>
where
I: Inet,
AP: FnOnce(&str) -> Result<I::Address, AddrParseError>,
NP: FnOnce(&str) -> Result<I, NetworkParseError>,
{
match s.rfind('/') {
None => host_parser(s),
Some(pos) => Ok(I::new(
address_parser(&s[0..pos])?,
s[pos + 1..].parse()?,
)?),
}
}

/// Parse [`Inet`] with custom address parser
///
/// If a '/' is found, parse trailing number as prefix length and leading address with `address_parser`.
/// Otherwise parse `address_parser` and treat as host (maximum prefix length).
pub fn parse_inet<I, AP>(s: &str, address_parser: AP) -> Result<I, NetworkParseError>
where
I: Inet,
AP: Fn(&str) -> Result<I::Address, AddrParseError>,
{
parse_inet_full(s, &address_parser, |s| {
Ok(I::new_host(address_parser(s)?))
})
}
21 changes: 20 additions & 1 deletion src/parsers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@
//! to be handled the functions here might help implementing custom
//! parsers.
mod combinators;
mod inetaddr;

pub use self::inetaddr::{inet_addr, parse_loose_ip, parse_loose_ipv4};
pub use self::{
combinators::{
parse_any_cidr_full_ignore_hostbits,
parse_any_cidr_full,
parse_any_cidr_ignore_hostbits,
parse_any_cidr,
parse_cidr_full_ignore_hostbits,
parse_cidr_full,
parse_cidr_ignore_hostbits,
parse_cidr,
parse_inet_full,
parse_inet,
},
inetaddr::{
inet_addr,
parse_loose_ip,
parse_loose_ipv4,
},
};

0 comments on commit 0784668

Please sign in to comment.