use std::net::IpAddr;
pub fn parse_ipnet(s: &str) -> Result<ipnet::IpNet, String> {
if let Ok(ipnet) = s.parse::<ipnet::IpNet>() {
return Ok(ipnet);
}
if let Ok(ip) = s.parse::<IpAddr>() {
let ipnet = match ip {
IpAddr::V4(ipv4) => ipnet::Ipv4Net::new(ipv4, 32)
.map(ipnet::IpNet::V4)
.map_err(|e| format!("Failed to create IPv4 network: {}", e))?,
IpAddr::V6(ipv6) => ipnet::Ipv6Net::new(ipv6, 128)
.map(ipnet::IpNet::V6)
.map_err(|e| format!("Failed to create IPv6 network: {}", e))?,
};
return Ok(ipnet);
}
Err(format!("Invalid IP address or CIDR notation: {}", s))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_ipv4_single_address() {
let result = parse_ipnet("192.168.1.100").unwrap();
assert_eq!(result.to_string(), "192.168.1.100/32");
}
#[test]
fn test_parse_ipv4_cidr() {
let result = parse_ipnet("10.0.0.0/8").unwrap();
assert_eq!(result.to_string(), "10.0.0.0/8");
}
#[test]
fn test_parse_ipv6_single_address() {
let result = parse_ipnet("2001:db8::1").unwrap();
assert_eq!(result.to_string(), "2001:db8::1/128");
}
#[test]
fn test_parse_ipv6_cidr() {
let result = parse_ipnet("2001:db8::/32").unwrap();
assert_eq!(result.to_string(), "2001:db8::/32");
}
#[test]
fn test_parse_localhost_ipv4() {
let result = parse_ipnet("127.0.0.1").unwrap();
assert_eq!(result.to_string(), "127.0.0.1/32");
}
#[test]
fn test_parse_localhost_ipv6() {
let result = parse_ipnet("::1").unwrap();
assert_eq!(result.to_string(), "::1/128");
}
#[test]
fn test_parse_private_networks() {
let cases = vec![
("192.168.1.0/24", "192.168.1.0/24"),
("10.0.0.0/8", "10.0.0.0/8"),
("172.16.0.0/12", "172.16.0.0/12"),
];
for (input, expected) in cases {
let result = parse_ipnet(input).unwrap();
assert_eq!(result.to_string(), expected);
}
}
#[test]
fn test_parse_invalid_ip() {
let result = parse_ipnet("not-an-ip");
assert!(
result.is_err(),
"Expected error for invalid IP, got: {:?}",
result
);
assert!(
result
.unwrap_err()
.contains("Invalid IP address or CIDR notation")
);
}
#[test]
fn test_parse_invalid_cidr() {
let result = parse_ipnet("192.168.1.0/33");
assert!(
result.is_err(),
"Expected error for invalid CIDR, got: {:?}",
result
);
}
#[test]
fn test_parse_empty_string() {
let result = parse_ipnet("");
assert!(
result.is_err(),
"Expected error for empty string, got: {:?}",
result
);
}
#[test]
fn test_parse_malformed_addresses() {
let invalid_cases = vec![
"192.168.1", "192.168.1.256", "2001:db8::gggg", "192.168.1.0/", "192.168.1.0/-1", ];
for invalid in invalid_cases {
let result = parse_ipnet(invalid);
assert!(result.is_err(), "Expected error for input: {}", invalid);
}
}
#[test]
fn test_parse_edge_case_cidrs() {
let cases = vec![
("0.0.0.0/0", "0.0.0.0/0"), ("::/0", "::/0"), ("192.168.1.0/32", "192.168.1.0/32"), ];
for (input, expected) in cases {
let result = parse_ipnet(input).unwrap();
assert_eq!(result.to_string(), expected);
}
}
}