1use core::fmt;
28use core::str::FromStr;
29
30#[cfg(feature = "serde")]
31use serde::{Deserialize, Serialize};
32
33#[cfg(feature = "arbitrary")]
34use arbitrary::Arbitrary;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
39pub enum IpAddrError {
40 InvalidFormat,
42}
43
44impl fmt::Display for IpAddrError {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 match self {
47 Self::InvalidFormat => write!(f, "invalid IP address format"),
48 }
49 }
50}
51
52#[cfg(feature = "std")]
53impl std::error::Error for IpAddrError {}
54
55#[repr(transparent)]
93#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
94#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
95#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
96pub struct IpAddr(core::net::IpAddr);
97
98impl IpAddr {
99 #[must_use]
111 #[inline]
112 pub const fn new(inner: core::net::IpAddr) -> Self {
113 Self(inner)
114 }
115
116 #[must_use]
129 #[inline]
130 pub const fn as_inner(&self) -> &core::net::IpAddr {
131 &self.0
132 }
133
134 #[must_use]
147 #[inline]
148 pub const fn into_inner(self) -> core::net::IpAddr {
149 self.0
150 }
151
152 #[must_use]
167 #[inline]
168 pub const fn is_ipv4(&self) -> bool {
169 self.0.is_ipv4()
170 }
171
172 #[must_use]
187 #[inline]
188 pub const fn is_ipv6(&self) -> bool {
189 self.0.is_ipv6()
190 }
191
192 #[must_use]
210 #[inline]
211 pub const fn is_loopback(&self) -> bool {
212 self.0.is_loopback()
213 }
214
215 #[must_use]
233 #[inline]
234 pub const fn is_unspecified(&self) -> bool {
235 self.0.is_unspecified()
236 }
237
238 #[must_use]
253 #[inline]
254 pub const fn is_multicast(&self) -> bool {
255 self.0.is_multicast()
256 }
257}
258
259impl From<core::net::IpAddr> for IpAddr {
260 fn from(inner: core::net::IpAddr) -> Self {
261 Self(inner)
262 }
263}
264
265impl From<IpAddr> for core::net::IpAddr {
266 fn from(addr: IpAddr) -> Self {
267 addr.0
268 }
269}
270
271impl FromStr for IpAddr {
272 type Err = IpAddrError;
273
274 fn from_str(s: &str) -> Result<Self, Self::Err> {
275 s.parse::<core::net::IpAddr>()
276 .map(Self)
277 .map_err(|_| IpAddrError::InvalidFormat)
278 }
279}
280
281impl fmt::Display for IpAddr {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 write!(f, "{}", self.0)
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290 use core::net::{Ipv4Addr, Ipv6Addr};
291
292 #[test]
293 fn test_new_ipv4() {
294 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
295 assert!(addr.is_ipv4());
296 assert!(!addr.is_ipv6());
297 }
298
299 #[test]
300 fn test_new_ipv6() {
301 let addr = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
302 assert!(addr.is_ipv6());
303 assert!(!addr.is_ipv4());
304 }
305
306 #[test]
307 fn test_as_inner() {
308 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
309 let inner = addr.as_inner();
310 assert_eq!(inner, &core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
311 }
312
313 #[test]
314 fn test_into_inner() {
315 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
316 let inner: core::net::IpAddr = addr.into_inner();
317 assert_eq!(inner, core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
318 }
319
320 #[test]
321 fn test_is_ipv4() {
322 let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
323 let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
324
325 assert!(v4.is_ipv4());
326 assert!(!v6.is_ipv4());
327 }
328
329 #[test]
330 fn test_is_ipv6() {
331 let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
332 let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
333
334 assert!(!v4.is_ipv6());
335 assert!(v6.is_ipv6());
336 }
337
338 #[test]
339 fn test_is_loopback() {
340 let v4_loopback = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
341 let v6_loopback = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
342 let v4_not_loopback = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
343
344 assert!(v4_loopback.is_loopback());
345 assert!(v6_loopback.is_loopback());
346 assert!(!v4_not_loopback.is_loopback());
347 }
348
349 #[test]
350 fn test_is_unspecified() {
351 let v4_unspecified = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::UNSPECIFIED));
352 let v6_unspecified = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::UNSPECIFIED));
353 let v4_specified = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
354
355 assert!(v4_unspecified.is_unspecified());
356 assert!(v6_unspecified.is_unspecified());
357 assert!(!v4_specified.is_unspecified());
358 }
359
360 #[test]
361 fn test_is_multicast() {
362 let v4_multicast = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(224, 0, 0, 1)));
363 let v6_multicast = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(
364 0xff00, 0, 0, 0, 0, 0, 0, 0,
365 )));
366 let v4_not_multicast = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
367
368 assert!(v4_multicast.is_multicast());
369 assert!(v6_multicast.is_multicast());
370 assert!(!v4_not_multicast.is_multicast());
371 }
372
373 #[test]
374 fn test_from_std_ipaddr() {
375 let std_addr = core::net::IpAddr::V4(Ipv4Addr::LOCALHOST);
376 let addr = IpAddr::from(std_addr);
377 assert!(addr.is_ipv4());
378 }
379
380 #[test]
381 fn test_into_std_ipaddr() {
382 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
383 let std_addr: core::net::IpAddr = addr.into();
384 assert_eq!(std_addr, core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
385 }
386
387 #[test]
388 fn test_from_str_ipv4() {
389 let addr: IpAddr = "127.0.0.1".parse().unwrap();
390 assert!(addr.is_ipv4());
391 assert!(addr.is_loopback());
392 }
393
394 #[test]
395 fn test_from_str_ipv6() {
396 let addr: IpAddr = "::1".parse().unwrap();
397 assert!(addr.is_ipv6());
398 assert!(addr.is_loopback());
399 }
400
401 #[test]
402 fn test_from_str_invalid() {
403 let result: Result<IpAddr, _> = "invalid".parse();
404 assert!(result.is_err());
405 }
406
407 #[test]
408 fn test_display_ipv4() {
409 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
410 assert_eq!(format!("{addr}"), "192.168.1.1");
411 }
412
413 #[test]
414 fn test_display_ipv6() {
415 let addr = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
416 assert_eq!(format!("{addr}"), "::1");
417 }
418
419 #[test]
420 fn test_equality() {
421 let addr1 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
422 let addr2 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
423 let addr3 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
424
425 assert_eq!(addr1, addr2);
426 assert_ne!(addr1, addr3);
427 }
428
429 #[test]
430 fn test_ordering() {
431 let addr1 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
432 let addr2 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
433
434 assert!(addr1 < addr2);
435 }
436
437 #[test]
438 fn test_copy() {
439 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
440 let addr2 = addr;
441 assert_eq!(addr, addr2);
442 }
443
444 #[test]
445 fn test_clone() {
446 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
447 let addr2 = addr;
448 assert_eq!(addr, addr2);
449 }
450
451 #[test]
452 fn test_error_display() {
453 let err = IpAddrError::InvalidFormat;
454 assert_eq!(format!("{err}"), "invalid IP address format");
455 }
456}