Skip to main content

use_cidr/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use std::net::IpAddr;
5
6/// Stores a parsed CIDR block.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct CidrBlock {
9    /// Normalized IP address component.
10    pub address: String,
11    /// Prefix length component.
12    pub prefix: u8,
13}
14
15/// Parses a CIDR block from text.
16pub fn parse_cidr(input: &str) -> Option<CidrBlock> {
17    let trimmed = input.trim();
18
19    if trimmed.is_empty() {
20        return None;
21    }
22
23    let (address, prefix) = trimmed.split_once('/')?;
24    let address = address.parse::<IpAddr>().ok()?;
25    let prefix = prefix.parse::<u8>().ok()?;
26
27    let valid_prefix = match address {
28        IpAddr::V4(_) => is_valid_ipv4_prefix(prefix),
29        IpAddr::V6(_) => is_valid_ipv6_prefix(prefix),
30    };
31
32    if !valid_prefix {
33        return None;
34    }
35
36    Some(CidrBlock {
37        address: address.to_string(),
38        prefix,
39    })
40}
41
42/// Formats a CIDR block.
43pub fn format_cidr(block: &CidrBlock) -> String {
44    format!("{}/{}", block.address, block.prefix)
45}
46
47/// Returns `true` when the input is a valid IPv4 or IPv6 CIDR block.
48pub fn is_cidr(input: &str) -> bool {
49    parse_cidr(input).is_some()
50}
51
52/// Returns `true` when the input is a valid IPv4 CIDR block.
53pub fn is_ipv4_cidr(input: &str) -> bool {
54    parse_cidr(input).is_some_and(|block| {
55        block
56            .address
57            .parse::<IpAddr>()
58            .is_ok_and(|address| matches!(address, IpAddr::V4(_)))
59    })
60}
61
62/// Returns `true` when the input is a valid IPv6 CIDR block.
63pub fn is_ipv6_cidr(input: &str) -> bool {
64    parse_cidr(input).is_some_and(|block| {
65        block
66            .address
67            .parse::<IpAddr>()
68            .is_ok_and(|address| matches!(address, IpAddr::V6(_)))
69    })
70}
71
72/// Returns `true` when the prefix is valid for IPv4.
73pub fn is_valid_ipv4_prefix(prefix: u8) -> bool {
74    prefix <= 32
75}
76
77/// Returns `true` when the prefix is valid for IPv6.
78pub fn is_valid_ipv6_prefix(prefix: u8) -> bool {
79    prefix <= 128
80}