blob: ed943b764f9e27a8428f2ff53a927866b3d911e3 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#[macro_use]
extern crate quote;
use net_types::ip::{IpAddress, SubnetError};
use proc_macro2::TokenStream;
use std::fmt::Formatter;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::num::ParseIntError;
use std::str::FromStr;
/// Declares a proc_macro with `name` using `generator` to generate any of `ty`.
macro_rules! declare_macro {
($name:ident, $generator:ident, $($ty:path),+) => {
#[proc_macro]
pub fn $name(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
Emitter::<$generator, $($ty),+>::emit(input).into()
}
}
}
/// Empty slot in an [`Emitter`].
struct Skip;
/// The mock error returned by [`Skip`].
#[derive(Debug)]
struct SkipError;
impl std::fmt::Display for SkipError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
impl std::error::Error for SkipError {}
impl FromStr for Skip {
type Err = SkipError;
fn from_str(_s: &str) -> Result<Self, Self::Err> {
Err(SkipError {})
}
}
/// Generates a [`TokenStream`] representation of `T`.
trait Generator<T> {
/// Generates the [`TokenStream`] for `input`.
fn generate(input: T) -> TokenStream;
/// Get an optional string representation of type `T`.
///
/// Used in error reporting when parsing fails.
fn type_str() -> Option<&'static str> {
Some(std::any::type_name::<T>())
}
}
impl<O> Generator<Skip> for O {
fn generate(_input: Skip) -> TokenStream {
unreachable!()
}
fn type_str() -> Option<&'static str> {
None
}
}
/// Attempts to emit the resulting [`TokenStream`] for `input` parsed as `T`.
fn try_emit<G, T>(input: &str) -> Result<TokenStream, T::Err>
where
G: Generator<T>,
T: FromStr,
{
Ok(G::generate(T::from_str(input)?))
}
/// Provides common structor for parsing types from [`TokenStream`]s and
/// emitting the resulting [`TokenStream`].
///
/// `Emitter` can parse any of `Tn` and pass into a [`Generator`] `G`, encoding
/// the common logic for declarations build from string parsing at compile-time.
struct Emitter<G, T1 = Skip, T2 = Skip, T3 = Skip, T4 = Skip> {
_g: std::marker::PhantomData<G>,
_t1: std::marker::PhantomData<T1>,
_t2: std::marker::PhantomData<T2>,
_t3: std::marker::PhantomData<T3>,
_t4: std::marker::PhantomData<T4>,
}
impl<G, T1, T2, T3, T4> Emitter<G, T1, T2, T3, T4>
where
G: Generator<T1> + Generator<T2> + Generator<T3> + Generator<T4>,
T1: FromStr,
T2: FromStr,
T3: FromStr,
T4: FromStr,
T1::Err: std::error::Error,
T2::Err: std::error::Error,
T3::Err: std::error::Error,
T4::Err: std::error::Error,
{
/// Emits the resulting [`TokenStream`] (or an error one) after attempting
/// to parse `input` into all of the `Emitter`'s types sequentially.
fn emit(input: proc_macro::TokenStream) -> TokenStream {
// Always require a string literal.
let input = proc_macro2::TokenStream::from(input);
let s = match syn::parse2::<syn::LitStr>(input.clone()) {
Ok(s) => s.value(),
Err(e) => return e.to_compile_error().into(),
};
match try_emit::<G, T1>(&s)
.or_else(|e1| try_emit::<G, T2>(&s).map_err(|e2| (e1, e2)))
.or_else(|(e1, e2)| try_emit::<G, T3>(&s).map_err(|e3| (e1, e2, e3)))
.or_else(|(e1, e2, e3)| try_emit::<G, T4>(&s).map_err(|e4| (e1, e2, e3, e4)))
{
Ok(ts) => ts,
Err((e1, e2, e3, e4)) => syn::Error::new_spanned(
input,
format!("failed to parse as {}", Self::error_str(&e1, &e2, &e3, &e4)),
)
.to_compile_error()
.into(),
}
}
/// Get the error string reported to the compiler when parsing fails with
/// this `Emitter`.
fn error_str(
e1: &dyn std::error::Error,
e2: &dyn std::error::Error,
e3: &dyn std::error::Error,
e4: &dyn std::error::Error,
) -> String {
[
(<G as Generator<T1>>::type_str(), e1),
(<G as Generator<T2>>::type_str(), e2),
(<G as Generator<T3>>::type_str(), e3),
(<G as Generator<T4>>::type_str(), e4),
]
.iter()
.filter_map(|(ts, e)| ts.map(|t| format!("{}: \"{}\"", t, e)))
.collect::<Vec<_>>()
.join(", or ")
}
}
/// Generator for `std` types.
enum StdGen {}
impl Generator<IpAddr> for StdGen {
fn generate(input: IpAddr) -> TokenStream {
let (t, inner) = match input {
IpAddr::V4(v4) => (quote! { V4 }, Self::generate(v4)),
IpAddr::V6(v6) => (quote! { V6 }, Self::generate(v6)),
};
quote! {
std::net::IpAddr::#t(#inner)
}
}
}
impl Generator<Ipv4Addr> for StdGen {
fn generate(input: Ipv4Addr) -> TokenStream {
let octets = input.octets();
quote! {
std::net::Ipv4Addr::new(#(#octets),*)
}
}
}
impl Generator<Ipv6Addr> for StdGen {
fn generate(input: Ipv6Addr) -> TokenStream {
let segments = input.segments();
quote! {
std::net::Ipv6Addr::new(#(#segments),*)
}
}
}
impl Generator<SocketAddr> for StdGen {
fn generate(input: SocketAddr) -> TokenStream {
let (t, inner) = match input {
SocketAddr::V4(v4) => (quote! { V4 }, Self::generate(v4)),
SocketAddr::V6(v6) => (quote! { V6 }, Self::generate(v6)),
};
quote! {
std::net::SocketAddr::#t(#inner)
}
}
}
impl Generator<SocketAddrV4> for StdGen {
fn generate(input: SocketAddrV4) -> TokenStream {
let addr = Self::generate(input.ip().clone());
let port = input.port();
quote! {
std::net::SocketAddrV4::new(#addr, #port)
}
}
}
impl Generator<SocketAddrV6> for StdGen {
fn generate(input: SocketAddrV6) -> TokenStream {
let addr = Self::generate(input.ip().clone());
let port = input.port();
let flowinfo = input.flowinfo();
let scope_id = input.scope_id();
quote! {
std::net::SocketAddrV6::new(#addr, #port, #flowinfo, #scope_id)
}
}
}
declare_macro!(std_ip, StdGen, IpAddr);
declare_macro!(std_ip_v4, StdGen, Ipv4Addr);
declare_macro!(std_ip_v6, StdGen, Ipv6Addr);
declare_macro!(std_socket_addr, StdGen, SocketAddr, SocketAddrV4);
declare_macro!(std_socket_addr_v4, StdGen, SocketAddrV4);
declare_macro!(std_socket_addr_v6, StdGen, SocketAddrV6);
/// Generator for FIDL types.
enum FidlGen {}
impl Generator<IpAddr> for FidlGen {
fn generate(input: IpAddr) -> TokenStream {
let (t, inner) = match input {
IpAddr::V4(v4) => (quote! { Ipv4 }, Self::generate(v4)),
IpAddr::V6(v6) => (quote! { Ipv6 }, Self::generate(v6)),
};
quote! {
fidl_fuchsia_net::IpAddress::#t(#inner)
}
}
}
impl Generator<Ipv4Addr> for FidlGen {
fn generate(input: Ipv4Addr) -> TokenStream {
let octets = input.octets();
quote! {
fidl_fuchsia_net::Ipv4Address{ addr: [#(#octets),*]}
}
}
}
impl Generator<Ipv6Addr> for FidlGen {
fn generate(input: Ipv6Addr) -> TokenStream {
let octets = input.octets();
quote! {
fidl_fuchsia_net::Ipv6Address{ addr: [#(#octets),*]}
}
}
}
impl Generator<SocketAddr> for FidlGen {
fn generate(input: SocketAddr) -> TokenStream {
let (t, inner) = match input {
SocketAddr::V4(v4) => (quote! { Ipv4 }, Self::generate(v4)),
SocketAddr::V6(v6) => (quote! { Ipv6 }, Self::generate(v6)),
};
quote! {
fidl_fuchsia_net::SocketAddress::#t(#inner)
}
}
}
impl Generator<SocketAddrV4> for FidlGen {
fn generate(input: SocketAddrV4) -> TokenStream {
let addr = Self::generate(input.ip().clone());
let port = input.port();
quote! {
fidl_fuchsia_net::Ipv4SocketAddress {
address: #addr,
port: #port
}
}
}
}
impl Generator<SocketAddrV6> for FidlGen {
fn generate(input: SocketAddrV6) -> TokenStream {
let addr = Self::generate(input.ip().clone());
let port = input.port();
let scope_id = u64::from(input.scope_id());
quote! {
fidl_fuchsia_net::Ipv6SocketAddress {
address: #addr,
port: #port,
zone_index: #scope_id
}
}
}
}
/// Helper struct to parse Mac addresses from string.
#[derive(Default)]
struct MacAddress([u8; 6]);
#[derive(thiserror::Error, Debug)]
enum MacParseError {
#[error("invalid length for MacAddress, should be 6")]
InvalidLength,
#[error("invalid byte length (\"{0}\") in MacAddress, should be 2")]
InvalidByte(String),
#[error("failed to parse byte \"{0}\": {1}")]
IntError(String, ParseIntError),
}
impl FromStr for MacAddress {
type Err = MacParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut mac = Self::default();
let Self(octets) = &mut mac;
let mut parse_iter = s.split(':');
let mut save_iter = octets.iter_mut();
loop {
match (parse_iter.next(), save_iter.next()) {
(Some(s), Some(b)) => {
if s.len() != 2 {
return Err(MacParseError::InvalidByte(s.to_string()));
}
*b = u8::from_str_radix(s, 16)
.map_err(|e| MacParseError::IntError(s.to_string(), e))?;
}
(None, Some(_)) | (Some(_), None) => break Err(MacParseError::InvalidLength),
(None, None) => break Ok(mac),
}
}
}
}
impl Generator<MacAddress> for FidlGen {
fn generate(input: MacAddress) -> TokenStream {
let MacAddress(octets) = input;
quote! {
fidl_fuchsia_net::MacAddress {
octets: [#(#octets),*]
}
}
}
}
#[derive(PartialEq, Debug)]
/// Helper struct to parse Cidr addresses from string.
struct IpAddressWithPrefix<A> {
address: A,
prefix: u8,
}
#[derive(thiserror::Error, PartialEq, Debug)]
enum IpAddressWithPrefixParseError {
#[error("missing address")]
MissingIp,
#[error("missing prefix length")]
MissingPrefix,
#[error("unexpected trailing data \"{0}\"")]
TrailingInformation(String),
#[error("failed to parse IP \"{0}\": {1}")]
IpParseError(String, std::net::AddrParseError),
#[error("failed to parse prefix \"{0}\": {1}")]
PrefixParseError(String, std::num::ParseIntError),
#[error("bad prefix value {0} for {1}")]
BadPrefix(u8, std::net::IpAddr),
}
impl FromStr for IpAddressWithPrefix<IpAddr> {
type Err = IpAddressWithPrefixParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parse_iter = s.split('/');
let ip_str = parse_iter.next().ok_or(IpAddressWithPrefixParseError::MissingIp)?;
let prefix_str = parse_iter.next().ok_or(IpAddressWithPrefixParseError::MissingPrefix)?;
if let Some(trailing) = parse_iter.next() {
return Err(IpAddressWithPrefixParseError::TrailingInformation(trailing.to_string()));
}
let address = std::net::IpAddr::from_str(ip_str)
.map_err(|e| IpAddressWithPrefixParseError::IpParseError(ip_str.to_string(), e))?;
let prefix = u8::from_str_radix(prefix_str, 10).map_err(|e| {
IpAddressWithPrefixParseError::PrefixParseError(prefix_str.to_string(), e)
})?;
let addr_len = 8 * match address {
std::net::IpAddr::V4(a) => a.octets().len(),
std::net::IpAddr::V6(a) => a.octets().len(),
};
if usize::from(prefix) > addr_len {
return Err(IpAddressWithPrefixParseError::BadPrefix(prefix, address));
}
Ok(Self { address, prefix })
}
}
impl Generator<IpAddressWithPrefix<IpAddr>> for FidlGen {
fn generate(input: IpAddressWithPrefix<IpAddr>) -> TokenStream {
let IpAddressWithPrefix { address, prefix } = input;
let address = Self::generate(address);
quote! {
fidl_fuchsia_net::Subnet {
addr: #address,
prefix_len: #prefix
}
}
}
}
impl Generator<IpAddressWithPrefix<IpAddr>> for NetGen {
fn generate(input: IpAddressWithPrefix<IpAddr>) -> TokenStream {
let IpAddressWithPrefix { address, prefix } = input;
let address = Self::generate(address);
// SAFETY: AddrSubnetEither's invariants were already checked.
quote! {
unsafe {
net_types::ip::AddrSubnetEither::new_unchecked(#address, #prefix)
}
}
}
}
impl Generator<IpAddressWithPrefix<Ipv4Addr>> for NetGen {
fn generate(input: IpAddressWithPrefix<Ipv4Addr>) -> TokenStream {
let IpAddressWithPrefix { address, prefix } = input;
let address = Self::generate(address);
// SAFETY: AddrSubnet's invariants were already checked.
quote! {
unsafe {
net_types::ip::AddrSubnet::<net_types::ip::Ipv4Addr>::new_unchecked(
#address, #prefix,
)
}
}
}
}
impl Generator<IpAddressWithPrefix<Ipv6Addr>> for NetGen {
fn generate(input: IpAddressWithPrefix<Ipv6Addr>) -> TokenStream {
let IpAddressWithPrefix { address, prefix } = input;
let address = Self::generate(address);
// SAFETY: AddrSubnet's invariants were already checked.
quote! {
unsafe {
net_types::ip::AddrSubnet::<net_types::ip::Ipv6Addr>::new_unchecked(
#address, #prefix,
)
}
}
}
}
#[derive(thiserror::Error, PartialEq, Debug)]
enum VersionedIpPrefixError {
#[error("wrong IP version: {0}")]
WrongVersion(IpAddr),
#[error("failed to parse")]
IpError(#[from] IpAddressWithPrefixParseError),
}
impl FromStr for IpAddressWithPrefix<Ipv4Addr> {
type Err = VersionedIpPrefixError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let IpAddressWithPrefix { address, prefix } = s.parse::<IpAddressWithPrefix<IpAddr>>()?;
match address {
IpAddr::V4(address) => Ok(Self { address, prefix }),
IpAddr::V6(address) => Err(VersionedIpPrefixError::WrongVersion(address.into())),
}
}
}
impl FromStr for IpAddressWithPrefix<Ipv6Addr> {
type Err = VersionedIpPrefixError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let IpAddressWithPrefix { address, prefix } = s.parse::<IpAddressWithPrefix<IpAddr>>()?;
match address {
IpAddr::V6(address) => Ok(Self { address, prefix }),
IpAddr::V4(address) => Err(VersionedIpPrefixError::WrongVersion(address.into())),
}
}
}
impl Generator<IpAddressWithPrefix<Ipv4Addr>> for FidlGen {
fn generate(input: IpAddressWithPrefix<Ipv4Addr>) -> TokenStream {
let IpAddressWithPrefix { address, prefix } = input;
let addr = Self::generate(address);
quote! {
fidl_fuchsia_net::Ipv4AddressWithPrefix {
addr: #addr,
prefix_len: #prefix,
}
}
}
}
impl Generator<IpAddressWithPrefix<Ipv6Addr>> for FidlGen {
fn generate(input: IpAddressWithPrefix<Ipv6Addr>) -> TokenStream {
let IpAddressWithPrefix { address, prefix } = input;
let addr = Self::generate(address);
quote! {
fidl_fuchsia_net::Ipv6AddressWithPrefix {
addr: #addr,
prefix_len: #prefix,
}
}
}
}
declare_macro!(fidl_ip, FidlGen, IpAddr);
declare_macro!(fidl_ip_v4, FidlGen, Ipv4Addr);
declare_macro!(fidl_ip_v4_with_prefix, FidlGen, IpAddressWithPrefix<Ipv4Addr>);
declare_macro!(fidl_ip_v6, FidlGen, Ipv6Addr);
declare_macro!(fidl_ip_v6_with_prefix, FidlGen, IpAddressWithPrefix<Ipv6Addr>);
declare_macro!(fidl_socket_addr, FidlGen, SocketAddr);
declare_macro!(fidl_socket_addr_v4, FidlGen, SocketAddrV4);
declare_macro!(fidl_socket_addr_v6, FidlGen, SocketAddrV6);
declare_macro!(fidl_mac, FidlGen, MacAddress);
declare_macro!(fidl_subnet, FidlGen, IpAddressWithPrefix<IpAddr>);
#[derive(PartialEq, Debug)]
/// Helper struct to parse Cidr addresses from string.
struct StrictSubnet<A> {
address: A,
prefix: u8,
}
#[derive(thiserror::Error, PartialEq, Debug)]
enum SubnetParseError {
#[error(transparent)]
IpError(#[from] VersionedIpPrefixError),
#[error("host bits set in {0}/{1}")]
HostBitsSet(IpAddr, u8),
}
impl FromStr for StrictSubnet<Ipv4Addr> {
type Err = SubnetParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let IpAddressWithPrefix { address, prefix } = s.parse::<IpAddressWithPrefix<Ipv4Addr>>()?;
match net_types::ip::Subnet::<net_types::ip::Ipv4Addr>::new(address.into(), prefix) {
Ok(_) => Ok(StrictSubnet { address, prefix }),
Err(SubnetError::HostBitsSet) => {
Err(SubnetParseError::HostBitsSet(address.into(), prefix))
}
Err(SubnetError::PrefixTooLong) => {
unreachable!("checked by IpAddressWithPrefix")
}
}
}
}
impl FromStr for StrictSubnet<Ipv6Addr> {
type Err = SubnetParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let IpAddressWithPrefix { address, prefix } = s.parse::<IpAddressWithPrefix<Ipv6Addr>>()?;
match net_types::ip::Subnet::<net_types::ip::Ipv6Addr>::new(address.into(), prefix) {
Ok(_) => Ok(StrictSubnet { address, prefix }),
Err(SubnetError::HostBitsSet) => {
Err(SubnetParseError::HostBitsSet(address.into(), prefix))
}
Err(SubnetError::PrefixTooLong) => {
unreachable!("checked by IpAddressWithPrefix")
}
}
}
}
/// Generator for net-types types.
enum NetGen {}
impl Generator<IpAddr> for NetGen {
fn generate(input: IpAddr) -> TokenStream {
let (t, inner) = match input {
IpAddr::V4(v4) => (quote! { V4 }, Self::generate(v4)),
IpAddr::V6(v6) => (quote! { V6 }, Self::generate(v6)),
};
quote! {
net_types::ip::IpAddr::#t(#inner)
}
}
}
impl Generator<Ipv4Addr> for NetGen {
fn generate(input: Ipv4Addr) -> TokenStream {
let octets = input.octets();
quote! {
net_types::ip::Ipv4Addr::new([#(#octets),*])
}
}
}
impl Generator<Ipv6Addr> for NetGen {
fn generate(input: Ipv6Addr) -> TokenStream {
let octets = input.octets();
quote! {
net_types::ip::Ipv6Addr::from_bytes([#(#octets),*])
}
}
}
impl Generator<MacAddress> for NetGen {
fn generate(input: MacAddress) -> TokenStream {
let MacAddress(octets) = input;
quote! {
net_types::ethernet::Mac::new([#(#octets),*])
}
}
}
impl<I> Generator<StrictSubnet<I>> for NetGen
where
Self: Generator<I>,
{
fn generate(input: StrictSubnet<I>) -> TokenStream {
let StrictSubnet { address, prefix } = input;
let network = Self::generate(address);
// SAFETY: Subnet's invariants were already checked.
quote! {
unsafe { net_types::ip::Subnet::new_unchecked(#network, #prefix) }
}
}
}
declare_macro!(net_ip, NetGen, IpAddr);
declare_macro!(net_ip_v4, NetGen, Ipv4Addr);
declare_macro!(net_ip_v6, NetGen, Ipv6Addr);
declare_macro!(net_mac, NetGen, MacAddress);
declare_macro!(net_subnet_v4, NetGen, StrictSubnet<Ipv4Addr>);
declare_macro!(net_subnet_v6, NetGen, StrictSubnet<Ipv6Addr>);
declare_macro!(net_addr_subnet_v4, NetGen, IpAddressWithPrefix<Ipv4Addr>);
declare_macro!(net_addr_subnet_v6, NetGen, IpAddressWithPrefix<Ipv6Addr>);
declare_macro!(net_addr_subnet, NetGen, IpAddressWithPrefix<IpAddr>);
fn net_prefix_length_impl<I: net_types::ip::Ip>(
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let input = proc_macro2::TokenStream::from(input);
let n = match syn::parse2::<syn::LitInt>(input.clone()).and_then(|n| n.base10_parse::<u8>()) {
Ok(n) => n,
Err(e) => return e.to_compile_error().into(),
};
if n > I::Addr::BYTES * 8 {
return syn::Error::new_spanned(
input,
format!("{n} is too long to be the prefix length for an {} address", I::NAME),
)
.to_compile_error()
.into();
}
let ip_version = match I::VERSION {
net_types::ip::IpVersion::V4 => quote! { net_types::ip::Ipv4 },
net_types::ip::IpVersion::V6 => quote! { net_types::ip::Ipv6 },
};
quote! {
// SAFETY: already checked that #n <= I::Addr::BYTES * 8.
unsafe {
net_types::ip::PrefixLength::<#ip_version>::new_unchecked(#n)
}
}
.into()
}
#[proc_macro]
pub fn net_prefix_length_v4(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
net_prefix_length_impl::<net_types::ip::Ipv4>(input)
}
#[proc_macro]
pub fn net_prefix_length_v6(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
net_prefix_length_impl::<net_types::ip::Ipv6>(input)
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
#[test]
fn ip_address_with_prefix_valid() {
assert_eq!(
"28.19.8.91/16".parse::<IpAddressWithPrefix<IpAddr>>(),
Ok(IpAddressWithPrefix { address: [28, 19, 8, 91].into(), prefix: 16 })
);
assert_eq!(
"184f:139:84::56:3456/38".parse::<IpAddressWithPrefix<IpAddr>>(),
Ok(IpAddressWithPrefix {
address: [0x184f, 0x0139, 0x0084, 0, 0, 0, 0x56, 0x3456].into(),
prefix: 38
})
);
assert_eq!(
"1.2.3.4/5".parse::<IpAddressWithPrefix<Ipv4Addr>>(),
Ok(IpAddressWithPrefix { address: 0x01020304.into(), prefix: 5 })
);
assert_eq!(
"5a:e8:6695::78:4521/108".parse::<IpAddressWithPrefix<Ipv6Addr>>(),
Ok(IpAddressWithPrefix {
address: [0x5a, 0xe8, 0x6695, 0, 0, 0, 0x78, 0x4521].into(),
prefix: 108
})
);
}
#[test]
fn ip_address_with_prefix_invalid() {
assert_matches!(
"5.6.7.8".parse::<IpAddressWithPrefix<IpAddr>>(),
Err(IpAddressWithPrefixParseError::MissingPrefix)
);
assert_matches!(
"192.168.0.1/12/x".parse::<IpAddressWithPrefix<IpAddr>>(),
Err(IpAddressWithPrefixParseError::TrailingInformation(_))
);
assert_matches!(
"12.9.9/6".parse::<IpAddressWithPrefix<IpAddr>>(),
Err(IpAddressWithPrefixParseError::IpParseError(_, _))
);
assert_matches!(
"192.168.0.1/ff".parse::<IpAddressWithPrefix<IpAddr>>(),
Err(IpAddressWithPrefixParseError::PrefixParseError(_, _))
);
assert_matches!(
"192.168.0.1/55".parse::<IpAddressWithPrefix<IpAddr>>(),
Err(IpAddressWithPrefixParseError::BadPrefix(55, _))
);
}
#[test]
fn ip_address_with_prefix_wrong_type() {
assert_eq!(
"28.19.8.91/16".parse::<IpAddressWithPrefix<Ipv6Addr>>(),
Err(VersionedIpPrefixError::WrongVersion([28, 19, 8, 91].into())),
);
assert_eq!(
"184f:139:84::56:3456/38".parse::<IpAddressWithPrefix<Ipv4Addr>>(),
Err(VersionedIpPrefixError::WrongVersion(
[0x184f, 0x0139, 0x0084, 0, 0, 0, 0x56, 0x3456].into()
))
);
}
#[test]
fn test_strict_subnet_prefix_valid() {
assert_eq!(
"192.168.0.1/32".parse::<StrictSubnet<Ipv4Addr>>(),
Ok(StrictSubnet { prefix: 32, address: [192, 168, 0, 1].into() })
);
assert_eq!(
"192.168.0.0/24".parse::<StrictSubnet<Ipv4Addr>>(),
Ok(StrictSubnet { prefix: 24, address: [192, 168, 0, 0].into() })
);
}
#[test]
fn test_strict_subnet_prefix_too_large() {
assert_eq!(
"192.168.0.0/33".parse::<StrictSubnet<Ipv4Addr>>(),
Err(SubnetParseError::IpError(VersionedIpPrefixError::IpError(
IpAddressWithPrefixParseError::BadPrefix(33, [192, 168, 0, 0].into())
)))
);
assert_eq!(
"ff:00:ff::/130".parse::<StrictSubnet<Ipv6Addr>>(),
Err(SubnetParseError::IpError(VersionedIpPrefixError::IpError(
IpAddressWithPrefixParseError::BadPrefix(130, "ff:00:ff::".parse().unwrap())
)))
);
}
#[test]
fn test_strict_subnet_prefix_host_bits_set() {
assert_eq!(
"192.168.0.0/8".parse::<StrictSubnet<Ipv4Addr>>(),
Err(SubnetParseError::HostBitsSet([192, 168, 0, 0].into(), 8))
);
assert_eq!(
"ff:00:ff::/14".parse::<StrictSubnet<Ipv6Addr>>(),
Err(SubnetParseError::HostBitsSet("ff:00:ff::".parse().unwrap(), 14))
);
}
}