Skip to main content

use_ip/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use std::net::IpAddr;
5
6/// Classifies a parsed IP literal.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum IpKind {
9    /// IPv4 address literal.
10    V4,
11    /// IPv6 address literal.
12    V6,
13    /// Unknown or invalid input.
14    Unknown,
15}
16
17/// Stores an original IP-like input with its detected kind.
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct IpParts {
20    /// Original input value.
21    pub input: String,
22    /// Detected IP kind.
23    pub kind: IpKind,
24}
25
26fn parse_ip(input: &str) -> Option<IpAddr> {
27    let trimmed = input.trim();
28
29    if trimmed.is_empty() {
30        return None;
31    }
32
33    trimmed.parse().ok()
34}
35
36/// Returns `true` when the input is a valid IPv4 or IPv6 literal.
37pub fn is_ip(input: &str) -> bool {
38    parse_ip(input).is_some()
39}
40
41/// Returns `true` when the input is a valid IPv4 literal.
42pub fn is_ipv4(input: &str) -> bool {
43    matches!(parse_ip(input), Some(IpAddr::V4(_)))
44}
45
46/// Returns `true` when the input is a valid IPv6 literal.
47pub fn is_ipv6(input: &str) -> bool {
48    matches!(parse_ip(input), Some(IpAddr::V6(_)))
49}
50
51/// Detects whether the input is IPv4, IPv6, or unknown.
52pub fn detect_ip_kind(input: &str) -> IpKind {
53    match parse_ip(input) {
54        Some(IpAddr::V4(_)) => IpKind::V4,
55        Some(IpAddr::V6(_)) => IpKind::V6,
56        None => IpKind::Unknown,
57    }
58}
59
60/// Normalizes a valid IP literal to Rust's canonical string form.
61pub fn normalize_ip(input: &str) -> Option<String> {
62    parse_ip(input).map(|address| address.to_string())
63}
64
65/// Returns `true` when the input is a loopback IP literal.
66pub fn is_loopback_ip(input: &str) -> bool {
67    parse_ip(input).is_some_and(|address| address.is_loopback())
68}
69
70/// Returns `true` when the input is an unspecified IP literal.
71pub fn is_unspecified_ip(input: &str) -> bool {
72    parse_ip(input).is_some_and(|address| address.is_unspecified())
73}
74
75/// Returns `true` when the input is a private IPv4 or unique-local IPv6 literal.
76pub fn is_private_ip(input: &str) -> bool {
77    match parse_ip(input) {
78        Some(IpAddr::V4(address)) => address.is_private(),
79        Some(IpAddr::V6(address)) => address.is_unique_local(),
80        None => false,
81    }
82}
83
84/// Returns `true` when the input is a link-local IPv4 or IPv6 literal.
85pub fn is_link_local_ip(input: &str) -> bool {
86    match parse_ip(input) {
87        Some(IpAddr::V4(address)) => address.is_link_local(),
88        Some(IpAddr::V6(address)) => address.is_unicast_link_local(),
89        None => false,
90    }
91}
92
93/// Returns `true` when the input is a multicast IP literal.
94pub fn is_multicast_ip(input: &str) -> bool {
95    parse_ip(input).is_some_and(|address| address.is_multicast())
96}