use core::fmt;
use core::str::FromStr;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "arbitrary")]
use arbitrary::Arbitrary;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub enum IpAddrError {
InvalidFormat,
}
impl fmt::Display for IpAddrError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidFormat => write!(f, "invalid IP address format"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for IpAddrError {}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
pub struct IpAddr(core::net::IpAddr);
impl IpAddr {
#[inline]
#[must_use]
pub const fn new(inner: core::net::IpAddr) -> Self {
Self(inner)
}
#[must_use]
#[inline]
pub const fn as_inner(&self) -> &core::net::IpAddr {
&self.0
}
#[must_use]
#[inline]
pub const fn into_inner(self) -> core::net::IpAddr {
self.0
}
#[must_use]
#[inline]
pub const fn is_ipv4(&self) -> bool {
self.0.is_ipv4()
}
#[must_use]
#[inline]
pub const fn is_ipv6(&self) -> bool {
self.0.is_ipv6()
}
#[must_use]
#[inline]
pub const fn is_loopback(&self) -> bool {
self.0.is_loopback()
}
#[must_use]
#[inline]
pub const fn is_unspecified(&self) -> bool {
self.0.is_unspecified()
}
#[must_use]
#[inline]
pub const fn is_multicast(&self) -> bool {
self.0.is_multicast()
}
}
impl From<core::net::IpAddr> for IpAddr {
fn from(inner: core::net::IpAddr) -> Self {
Self(inner)
}
}
impl From<IpAddr> for core::net::IpAddr {
fn from(addr: IpAddr) -> Self {
addr.0
}
}
impl FromStr for IpAddr {
type Err = IpAddrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<core::net::IpAddr>()
.map(Self)
.map_err(|_| IpAddrError::InvalidFormat)
}
}
impl fmt::Display for IpAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::net::{Ipv4Addr, Ipv6Addr};
#[test]
fn test_new_ipv4() {
let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
assert!(addr.is_ipv4());
assert!(!addr.is_ipv6());
}
#[test]
fn test_new_ipv6() {
let addr = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
assert!(addr.is_ipv6());
assert!(!addr.is_ipv4());
}
#[test]
fn test_as_inner() {
let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
let inner = addr.as_inner();
assert_eq!(inner, &core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
}
#[test]
fn test_into_inner() {
let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
let inner: core::net::IpAddr = addr.into_inner();
assert_eq!(inner, core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
}
#[test]
fn test_is_ipv4() {
let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
assert!(v4.is_ipv4());
assert!(!v6.is_ipv4());
}
#[test]
fn test_is_ipv6() {
let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
assert!(!v4.is_ipv6());
assert!(v6.is_ipv6());
}
#[test]
fn test_is_loopback() {
let v4_loopback = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
let v6_loopback = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
let v4_not_loopback = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
assert!(v4_loopback.is_loopback());
assert!(v6_loopback.is_loopback());
assert!(!v4_not_loopback.is_loopback());
}
#[test]
fn test_is_unspecified() {
let v4_unspecified = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::UNSPECIFIED));
let v6_unspecified = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::UNSPECIFIED));
let v4_specified = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
assert!(v4_unspecified.is_unspecified());
assert!(v6_unspecified.is_unspecified());
assert!(!v4_specified.is_unspecified());
}
#[test]
fn test_is_multicast() {
let v4_multicast = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(224, 0, 0, 1)));
let v6_multicast = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(
0xff00, 0, 0, 0, 0, 0, 0, 0,
)));
let v4_not_multicast = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
assert!(v4_multicast.is_multicast());
assert!(v6_multicast.is_multicast());
assert!(!v4_not_multicast.is_multicast());
}
#[test]
fn test_from_std_ipaddr() {
let std_addr = core::net::IpAddr::V4(Ipv4Addr::LOCALHOST);
let addr = IpAddr::from(std_addr);
assert!(addr.is_ipv4());
}
#[test]
fn test_into_std_ipaddr() {
let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
let std_addr: core::net::IpAddr = addr.into();
assert_eq!(std_addr, core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
}
#[test]
fn test_from_str_ipv4() {
let addr: IpAddr = "127.0.0.1".parse().unwrap();
assert!(addr.is_ipv4());
assert!(addr.is_loopback());
}
#[test]
fn test_from_str_ipv6() {
let addr: IpAddr = "::1".parse().unwrap();
assert!(addr.is_ipv6());
assert!(addr.is_loopback());
}
#[test]
fn test_from_str_invalid() {
let result: Result<IpAddr, _> = "invalid".parse();
assert!(result.is_err());
}
#[test]
fn test_display_ipv4() {
let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
assert_eq!(format!("{addr}"), "192.168.1.1");
}
#[test]
fn test_display_ipv6() {
let addr = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
assert_eq!(format!("{addr}"), "::1");
}
#[test]
fn test_equality() {
let addr1 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
let addr2 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
let addr3 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
assert_eq!(addr1, addr2);
assert_ne!(addr1, addr3);
}
#[test]
fn test_ordering() {
let addr1 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
let addr2 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
assert!(addr1 < addr2);
}
#[test]
fn test_copy() {
let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
let addr2 = addr;
assert_eq!(addr, addr2);
}
#[test]
fn test_clone() {
let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
let addr2 = addr;
assert_eq!(addr, addr2);
}
#[test]
fn test_error_display() {
let err = IpAddrError::InvalidFormat;
assert_eq!(format!("{err}"), "invalid IP address format");
}
}