#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use std::net::IpAddr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IpKind {
V4,
V6,
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IpParts {
pub input: String,
pub kind: IpKind,
}
fn parse_ip(input: &str) -> Option<IpAddr> {
let trimmed = input.trim();
if trimmed.is_empty() {
return None;
}
trimmed.parse().ok()
}
pub fn is_ip(input: &str) -> bool {
parse_ip(input).is_some()
}
pub fn is_ipv4(input: &str) -> bool {
matches!(parse_ip(input), Some(IpAddr::V4(_)))
}
pub fn is_ipv6(input: &str) -> bool {
matches!(parse_ip(input), Some(IpAddr::V6(_)))
}
pub fn detect_ip_kind(input: &str) -> IpKind {
match parse_ip(input) {
Some(IpAddr::V4(_)) => IpKind::V4,
Some(IpAddr::V6(_)) => IpKind::V6,
None => IpKind::Unknown,
}
}
pub fn normalize_ip(input: &str) -> Option<String> {
parse_ip(input).map(|address| address.to_string())
}
pub fn is_loopback_ip(input: &str) -> bool {
parse_ip(input).is_some_and(|address| address.is_loopback())
}
pub fn is_unspecified_ip(input: &str) -> bool {
parse_ip(input).is_some_and(|address| address.is_unspecified())
}
pub fn is_private_ip(input: &str) -> bool {
match parse_ip(input) {
Some(IpAddr::V4(address)) => address.is_private(),
Some(IpAddr::V6(address)) => address.is_unique_local(),
None => false,
}
}
pub fn is_link_local_ip(input: &str) -> bool {
match parse_ip(input) {
Some(IpAddr::V4(address)) => address.is_link_local(),
Some(IpAddr::V6(address)) => address.is_unicast_link_local(),
None => false,
}
}
pub fn is_multicast_ip(input: &str) -> bool {
parse_ip(input).is_some_and(|address| address.is_multicast())
}