Skip to main content

bare_types/net/
ipaddr.rs

1//! IP address type for network programming.
2//!
3//! This module provides a type-safe abstraction for IP addresses (IPv4 and IPv6),
4//! wrapping the standard library's `IpAddr` type.
5//!
6//! # Examples
7//!
8//! ```rust
9//! use bare_types::net::IpAddr;
10//! use core::net::{Ipv4Addr, Ipv6Addr};
11//!
12//! // Create an IPv4 address
13//! let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
14//! assert!(v4.is_ipv4());
15//! assert!(v4.is_loopback());
16//!
17//! // Create an IPv6 address
18//! let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
19//! assert!(v6.is_ipv6());
20//! assert!(v6.is_loopback());
21//!
22//! // Parse from string
23//! let addr: IpAddr = "192.168.1.1".parse()?;
24//! # Ok::<(), bare_types::net::IpAddrError>(())
25//! ```
26
27use 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/// Error type for IP address parsing.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
39pub enum IpAddrError {
40    /// Invalid IP address format
41    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/// An IP address (IPv4 or IPv6).
56///
57/// This type wraps the standard library's `IpAddr` to provide type safety
58/// and consistent API with other types in this crate. It uses the newtype
59/// pattern with `#[repr(transparent)]` for zero-cost abstraction.
60///
61/// # Invariants
62///
63/// - The inner value is always a valid IP address
64/// - IPv4 addresses are 4 octets (0-255 each)
65/// - IPv6 addresses are 8 groups of 16 bits
66///
67/// # Examples
68///
69/// ```rust
70/// use bare_types::net::IpAddr;
71/// use core::net::{Ipv4Addr, Ipv6Addr};
72///
73/// // Create an IPv4 address
74/// let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
75/// assert!(v4.is_ipv4());
76/// assert!(v4.is_loopback());
77///
78/// // Create an IPv6 address
79/// let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
80/// assert!(v6.is_ipv6());
81/// assert!(v6.is_loopback());
82///
83/// // Access the inner value
84/// let inner: core::net::IpAddr = v4.into_inner();
85/// assert_eq!(inner, core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
86///
87/// // Parse from string
88/// let addr: IpAddr = "192.168.1.1".parse()?;
89/// assert!(addr.is_ipv4());
90/// # Ok::<(), bare_types::net::IpAddrError>(())
91/// ```
92#[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    /// Creates a new IP address from a standard library `IpAddr`.
100    ///
101    /// # Examples
102    ///
103    /// ```rust
104    /// use bare_types::net::IpAddr;
105    /// use core::net::Ipv4Addr;
106    ///
107    /// let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
108    /// assert!(addr.is_ipv4());
109    /// ```
110    #[must_use]
111    #[inline]
112    pub const fn new(inner: core::net::IpAddr) -> Self {
113        Self(inner)
114    }
115
116    /// Returns a reference to the underlying `IpAddr`.
117    ///
118    /// # Examples
119    ///
120    /// ```rust
121    /// use bare_types::net::IpAddr;
122    /// use core::net::Ipv4Addr;
123    ///
124    /// let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
125    /// let inner = addr.as_inner();
126    /// assert_eq!(inner, &core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
127    /// ```
128    #[must_use]
129    #[inline]
130    pub const fn as_inner(&self) -> &core::net::IpAddr {
131        &self.0
132    }
133
134    /// Consumes this IP address and returns the underlying `IpAddr`.
135    ///
136    /// # Examples
137    ///
138    /// ```rust
139    /// use bare_types::net::IpAddr;
140    /// use core::net::Ipv4Addr;
141    ///
142    /// let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
143    /// let inner: core::net::IpAddr = addr.into_inner();
144    /// assert_eq!(inner, core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
145    /// ```
146    #[must_use]
147    #[inline]
148    pub const fn into_inner(self) -> core::net::IpAddr {
149        self.0
150    }
151
152    /// Returns `true` if this is an IPv4 address.
153    ///
154    /// # Examples
155    ///
156    /// ```rust
157    /// use bare_types::net::IpAddr;
158    /// use core::net::{Ipv4Addr, Ipv6Addr};
159    ///
160    /// let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
161    /// let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
162    ///
163    /// assert!(v4.is_ipv4());
164    /// assert!(!v6.is_ipv4());
165    /// ```
166    #[must_use]
167    #[inline]
168    pub const fn is_ipv4(&self) -> bool {
169        self.0.is_ipv4()
170    }
171
172    /// Returns `true` if this is an IPv6 address.
173    ///
174    /// # Examples
175    ///
176    /// ```rust
177    /// use bare_types::net::IpAddr;
178    /// use core::net::{Ipv4Addr, Ipv6Addr};
179    ///
180    /// let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
181    /// let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
182    ///
183    /// assert!(!v4.is_ipv6());
184    /// assert!(v6.is_ipv6());
185    /// ```
186    #[must_use]
187    #[inline]
188    pub const fn is_ipv6(&self) -> bool {
189        self.0.is_ipv6()
190    }
191
192    /// Returns `true` if this is a loopback address.
193    ///
194    /// For IPv4, this is 127.0.0.1.
195    /// For IPv6, this is `::1`.
196    ///
197    /// # Examples
198    ///
199    /// ```rust
200    /// use bare_types::net::IpAddr;
201    /// use core::net::{Ipv4Addr, Ipv6Addr};
202    ///
203    /// let v4_loopback = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
204    /// let v6_loopback = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
205    ///
206    /// assert!(v4_loopback.is_loopback());
207    /// assert!(v6_loopback.is_loopback());
208    /// ```
209    #[must_use]
210    #[inline]
211    pub const fn is_loopback(&self) -> bool {
212        self.0.is_loopback()
213    }
214
215    /// Returns `true` if this is an unspecified address.
216    ///
217    /// For IPv4, this is 0.0.0.0.
218    /// For IPv6, this is ::.
219    ///
220    /// # Examples
221    ///
222    /// ```rust
223    /// use bare_types::net::IpAddr;
224    /// use core::net::{Ipv4Addr, Ipv6Addr};
225    ///
226    /// let v4_unspecified = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)));
227    /// let v6_unspecified = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)));
228    ///
229    /// assert!(v4_unspecified.is_unspecified());
230    /// assert!(v6_unspecified.is_unspecified());
231    /// ```
232    #[must_use]
233    #[inline]
234    pub const fn is_unspecified(&self) -> bool {
235        self.0.is_unspecified()
236    }
237
238    /// Returns `true` if this is a multicast address.
239    ///
240    /// # Examples
241    ///
242    /// ```rust
243    /// use bare_types::net::IpAddr;
244    /// use core::net::{Ipv4Addr, Ipv6Addr};
245    ///
246    /// let v4_multicast = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(224, 0, 0, 1)));
247    /// let v6_multicast = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)));
248    ///
249    /// assert!(v4_multicast.is_multicast());
250    /// assert!(v6_multicast.is_multicast());
251    /// ```
252    #[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}