#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use std::net::IpAddr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CidrBlock {
pub address: String,
pub prefix: u8,
}
pub fn parse_cidr(input: &str) -> Option<CidrBlock> {
let trimmed = input.trim();
if trimmed.is_empty() {
return None;
}
let (address, prefix) = trimmed.split_once('/')?;
let address = address.parse::<IpAddr>().ok()?;
let prefix = prefix.parse::<u8>().ok()?;
let valid_prefix = match address {
IpAddr::V4(_) => is_valid_ipv4_prefix(prefix),
IpAddr::V6(_) => is_valid_ipv6_prefix(prefix),
};
if !valid_prefix {
return None;
}
Some(CidrBlock {
address: address.to_string(),
prefix,
})
}
pub fn format_cidr(block: &CidrBlock) -> String {
format!("{}/{}", block.address, block.prefix)
}
pub fn is_cidr(input: &str) -> bool {
parse_cidr(input).is_some()
}
pub fn is_ipv4_cidr(input: &str) -> bool {
parse_cidr(input).is_some_and(|block| {
block
.address
.parse::<IpAddr>()
.is_ok_and(|address| matches!(address, IpAddr::V4(_)))
})
}
pub fn is_ipv6_cidr(input: &str) -> bool {
parse_cidr(input).is_some_and(|block| {
block
.address
.parse::<IpAddr>()
.is_ok_and(|address| matches!(address, IpAddr::V6(_)))
})
}
pub fn is_valid_ipv4_prefix(prefix: u8) -> bool {
prefix <= 32
}
pub fn is_valid_ipv6_prefix(prefix: u8) -> bool {
prefix <= 128
}