use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
fn parse_bsd_part(part: &str) -> Result<u32, String> {
let part = part.trim();
if part.is_empty() {
return Err("empty part".into());
}
let (base, start_idx) = if part.starts_with("0x") || part.starts_with("0X") {
(16, 2)
} else if part.starts_with('0') && part.len() > 1 {
(8, 1)
} else {
(10, 0)
};
let num_str = &part[start_idx..];
if (base == 8 || base == 16) && num_str.is_empty() {
return Err("invalid number format".into());
}
u32::from_str_radix(num_str, base).map_err(|e| format!("invalid number: {}", e))
}
pub fn parse_bsd_ipv4(s: &str) -> Result<Ipv4Addr, String> {
let parts: Vec<&str> = s.split('.').collect();
let n = parts.len();
if !(1..=4).contains(&n) {
return Err("invalid number of parts".into());
}
let mut parsed_parts: Vec<u32> = Vec::with_capacity(n);
for part in parts {
let parsed = parse_bsd_part(part)?;
parsed_parts.push(parsed);
}
let addr: u32 = match n {
4 => {
if parsed_parts[0] > 0xFF
|| parsed_parts[1] > 0xFF
|| parsed_parts[2] > 0xFF
|| parsed_parts[3] > 0xFF
{
return Err("invalid IP part out of range".into());
}
(parsed_parts[0] << 24)
| (parsed_parts[1] << 16)
| (parsed_parts[2] << 8)
| parsed_parts[3]
}
3 => {
if parsed_parts[0] > 0xFF || parsed_parts[1] > 0xFF || parsed_parts[2] > 0xFFFF {
return Err("invalid IP part out of range".into());
}
(parsed_parts[0] << 24) | (parsed_parts[1] << 16) | parsed_parts[2]
}
2 => {
if parsed_parts[0] > 0xFF || parsed_parts[1] > 0xFFFFFF {
return Err("invalid IP part out of range".into());
}
(parsed_parts[0] << 24) | parsed_parts[1]
}
1 => parsed_parts[0],
_ => {
return Err("invalid number of parts".into());
}
};
Ok(Ipv4Addr::from(addr))
}
pub fn parse_ip(s: &str) -> Result<IpAddr, String> {
if let Ok(ip) = s.parse::<Ipv6Addr>() {
return Ok(IpAddr::V6(ip));
}
if let Ok(ipv4) = parse_bsd_ipv4(s) {
return Ok(IpAddr::V4(ipv4));
}
Err(format!("invalid IP address: {}", s))
}