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))]
39#[non_exhaustive]
40pub enum IpAddrError {
41 /// Invalid IP address format
42 ///
43 /// The input string is not a valid IPv4 or IPv6 address.
44 /// IPv4 addresses must be in dotted decimal notation (e.g., "192.168.1.1").
45 /// IPv6 addresses must be in colon-separated hexadecimal notation (e.g., "`::1`").
46 InvalidFormat,
47}
48
49impl fmt::Display for IpAddrError {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 Self::InvalidFormat => write!(f, "invalid IP address format"),
53 }
54 }
55}
56
57#[cfg(feature = "std")]
58impl std::error::Error for IpAddrError {}
59
60/// An IP address (IPv4 or IPv6).
61///
62/// This type wraps the standard library's `IpAddr` to provide type safety
63/// and consistent API with other types in this crate. It uses the newtype
64/// pattern with `#[repr(transparent)]` for zero-cost abstraction.
65///
66/// # Invariants
67///
68/// - The inner value is always a valid IP address
69/// - IPv4 addresses are 4 octets (0-255 each)
70/// - IPv6 addresses are 8 groups of 16 bits
71///
72/// # Examples
73///
74/// ```rust
75/// use bare_types::net::IpAddr;
76/// use core::net::{Ipv4Addr, Ipv6Addr};
77///
78/// // Create an IPv4 address
79/// let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
80/// assert!(v4.is_ipv4());
81/// assert!(v4.is_loopback());
82///
83/// // Create an IPv6 address
84/// let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
85/// assert!(v6.is_ipv6());
86/// assert!(v6.is_loopback());
87///
88/// // Access the inner value
89/// let inner: core::net::IpAddr = v4.into_inner();
90/// assert_eq!(inner, core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
91///
92/// // Parse from string
93/// let addr: IpAddr = "192.168.1.1".parse()?;
94/// assert!(addr.is_ipv4());
95/// # Ok::<(), bare_types::net::IpAddrError>(())
96/// ```
97#[repr(transparent)]
98#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
99#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
100#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
101pub struct IpAddr(core::net::IpAddr);
102
103impl IpAddr {
104 /// Creates a new IP address from a standard library `IpAddr`.
105 ///
106 /// # Examples
107 ///
108 /// ```rust
109 /// use bare_types::net::IpAddr;
110 /// use core::net::Ipv4Addr;
111 ///
112 /// let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
113 /// assert!(addr.is_ipv4());
114 /// ```
115 #[must_use]
116 #[inline]
117 pub const fn new(inner: core::net::IpAddr) -> Self {
118 Self(inner)
119 }
120
121 /// Returns a reference to the underlying `IpAddr`.
122 ///
123 /// # Examples
124 ///
125 /// ```rust
126 /// use bare_types::net::IpAddr;
127 /// use core::net::Ipv4Addr;
128 ///
129 /// let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
130 /// let inner = addr.as_inner();
131 /// assert_eq!(inner, &core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
132 /// ```
133 #[must_use]
134 #[inline]
135 pub const fn as_inner(&self) -> &core::net::IpAddr {
136 &self.0
137 }
138
139 /// Consumes this IP address and returns the underlying `IpAddr`.
140 ///
141 /// # Examples
142 ///
143 /// ```rust
144 /// use bare_types::net::IpAddr;
145 /// use core::net::Ipv4Addr;
146 ///
147 /// let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
148 /// let inner: core::net::IpAddr = addr.into_inner();
149 /// assert_eq!(inner, core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
150 /// ```
151 #[must_use]
152 #[inline]
153 pub const fn into_inner(self) -> core::net::IpAddr {
154 self.0
155 }
156
157 /// Returns `true` if this is an IPv4 address.
158 ///
159 /// # Examples
160 ///
161 /// ```rust
162 /// use bare_types::net::IpAddr;
163 /// use core::net::{Ipv4Addr, Ipv6Addr};
164 ///
165 /// let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
166 /// let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
167 ///
168 /// assert!(v4.is_ipv4());
169 /// assert!(!v6.is_ipv4());
170 /// ```
171 #[must_use]
172 #[inline]
173 pub const fn is_ipv4(&self) -> bool {
174 self.0.is_ipv4()
175 }
176
177 /// Returns `true` if this is an IPv6 address.
178 ///
179 /// # Examples
180 ///
181 /// ```rust
182 /// use bare_types::net::IpAddr;
183 /// use core::net::{Ipv4Addr, Ipv6Addr};
184 ///
185 /// let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
186 /// let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
187 ///
188 /// assert!(!v4.is_ipv6());
189 /// assert!(v6.is_ipv6());
190 /// ```
191 #[must_use]
192 #[inline]
193 pub const fn is_ipv6(&self) -> bool {
194 self.0.is_ipv6()
195 }
196
197 /// Returns `true` if this is a loopback address.
198 ///
199 /// For IPv4, this is 127.0.0.1.
200 /// For IPv6, this is `::1`.
201 ///
202 /// # Examples
203 ///
204 /// ```rust
205 /// use bare_types::net::IpAddr;
206 /// use core::net::{Ipv4Addr, Ipv6Addr};
207 ///
208 /// let v4_loopback = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
209 /// let v6_loopback = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
210 ///
211 /// assert!(v4_loopback.is_loopback());
212 /// assert!(v6_loopback.is_loopback());
213 /// ```
214 #[must_use]
215 #[inline]
216 pub const fn is_loopback(&self) -> bool {
217 self.0.is_loopback()
218 }
219
220 /// Returns `true` if this is an unspecified address.
221 ///
222 /// For IPv4, this is 0.0.0.0.
223 /// For IPv6, this is ::.
224 ///
225 /// # Examples
226 ///
227 /// ```rust
228 /// use bare_types::net::IpAddr;
229 /// use core::net::{Ipv4Addr, Ipv6Addr};
230 ///
231 /// let v4_unspecified = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)));
232 /// let v6_unspecified = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)));
233 ///
234 /// assert!(v4_unspecified.is_unspecified());
235 /// assert!(v6_unspecified.is_unspecified());
236 /// ```
237 #[must_use]
238 #[inline]
239 pub const fn is_unspecified(&self) -> bool {
240 self.0.is_unspecified()
241 }
242
243 /// Returns `true` if this is a multicast address.
244 ///
245 /// # Examples
246 ///
247 /// ```rust
248 /// use bare_types::net::IpAddr;
249 /// use core::net::{Ipv4Addr, Ipv6Addr};
250 ///
251 /// let v4_multicast = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(224, 0, 0, 1)));
252 /// let v6_multicast = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)));
253 ///
254 /// assert!(v4_multicast.is_multicast());
255 /// assert!(v6_multicast.is_multicast());
256 /// ```
257 #[must_use]
258 #[inline]
259 pub const fn is_multicast(&self) -> bool {
260 self.0.is_multicast()
261 }
262}
263
264impl From<core::net::IpAddr> for IpAddr {
265 fn from(inner: core::net::IpAddr) -> Self {
266 Self(inner)
267 }
268}
269
270impl From<IpAddr> for core::net::IpAddr {
271 fn from(addr: IpAddr) -> Self {
272 addr.0
273 }
274}
275
276impl FromStr for IpAddr {
277 type Err = IpAddrError;
278
279 fn from_str(s: &str) -> Result<Self, Self::Err> {
280 s.parse::<core::net::IpAddr>()
281 .map(Self)
282 .map_err(|_| IpAddrError::InvalidFormat)
283 }
284}
285
286impl fmt::Display for IpAddr {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 write!(f, "{}", self.0)
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295 use core::net::{Ipv4Addr, Ipv6Addr};
296
297 #[test]
298 fn test_new_ipv4() {
299 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
300 assert!(addr.is_ipv4());
301 assert!(!addr.is_ipv6());
302 }
303
304 #[test]
305 fn test_new_ipv6() {
306 let addr = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
307 assert!(addr.is_ipv6());
308 assert!(!addr.is_ipv4());
309 }
310
311 #[test]
312 fn test_as_inner() {
313 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
314 let inner = addr.as_inner();
315 assert_eq!(inner, &core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
316 }
317
318 #[test]
319 fn test_into_inner() {
320 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
321 let inner: core::net::IpAddr = addr.into_inner();
322 assert_eq!(inner, core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
323 }
324
325 #[test]
326 fn test_is_ipv4() {
327 let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
328 let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
329
330 assert!(v4.is_ipv4());
331 assert!(!v6.is_ipv4());
332 }
333
334 #[test]
335 fn test_is_ipv6() {
336 let v4 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
337 let v6 = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
338
339 assert!(!v4.is_ipv6());
340 assert!(v6.is_ipv6());
341 }
342
343 #[test]
344 fn test_is_loopback() {
345 let v4_loopback = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
346 let v6_loopback = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
347 let v4_not_loopback = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
348
349 assert!(v4_loopback.is_loopback());
350 assert!(v6_loopback.is_loopback());
351 assert!(!v4_not_loopback.is_loopback());
352 }
353
354 #[test]
355 fn test_is_unspecified() {
356 let v4_unspecified = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::UNSPECIFIED));
357 let v6_unspecified = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::UNSPECIFIED));
358 let v4_specified = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
359
360 assert!(v4_unspecified.is_unspecified());
361 assert!(v6_unspecified.is_unspecified());
362 assert!(!v4_specified.is_unspecified());
363 }
364
365 #[test]
366 fn test_is_multicast() {
367 let v4_multicast = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(224, 0, 0, 1)));
368 let v6_multicast = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::new(
369 0xff00, 0, 0, 0, 0, 0, 0, 0,
370 )));
371 let v4_not_multicast = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
372
373 assert!(v4_multicast.is_multicast());
374 assert!(v6_multicast.is_multicast());
375 assert!(!v4_not_multicast.is_multicast());
376 }
377
378 #[test]
379 fn test_from_std_ipaddr() {
380 let std_addr = core::net::IpAddr::V4(Ipv4Addr::LOCALHOST);
381 let addr = IpAddr::from(std_addr);
382 assert!(addr.is_ipv4());
383 }
384
385 #[test]
386 fn test_into_std_ipaddr() {
387 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
388 let std_addr: core::net::IpAddr = addr.into();
389 assert_eq!(std_addr, core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
390 }
391
392 #[test]
393 fn test_from_str_ipv4() {
394 let addr: IpAddr = "127.0.0.1".parse().unwrap();
395 assert!(addr.is_ipv4());
396 assert!(addr.is_loopback());
397 }
398
399 #[test]
400 fn test_from_str_ipv6() {
401 let addr: IpAddr = "::1".parse().unwrap();
402 assert!(addr.is_ipv6());
403 assert!(addr.is_loopback());
404 }
405
406 #[test]
407 fn test_from_str_invalid() {
408 let result: Result<IpAddr, _> = "invalid".parse();
409 assert!(result.is_err());
410 }
411
412 #[test]
413 fn test_display_ipv4() {
414 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
415 assert_eq!(format!("{addr}"), "192.168.1.1");
416 }
417
418 #[test]
419 fn test_display_ipv6() {
420 let addr = IpAddr::new(core::net::IpAddr::V6(Ipv6Addr::LOCALHOST));
421 assert_eq!(format!("{addr}"), "::1");
422 }
423
424 #[test]
425 fn test_equality() {
426 let addr1 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
427 let addr2 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
428 let addr3 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
429
430 assert_eq!(addr1, addr2);
431 assert_ne!(addr1, addr3);
432 }
433
434 #[test]
435 fn test_ordering() {
436 let addr1 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
437 let addr2 = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)));
438
439 assert!(addr1 < addr2);
440 }
441
442 #[test]
443 fn test_copy() {
444 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
445 let addr2 = addr;
446 assert_eq!(addr, addr2);
447 }
448
449 #[test]
450 fn test_clone() {
451 let addr = IpAddr::new(core::net::IpAddr::V4(Ipv4Addr::LOCALHOST));
452 let addr2 = addr;
453 assert_eq!(addr, addr2);
454 }
455
456 #[test]
457 fn test_error_display() {
458 let err = IpAddrError::InvalidFormat;
459 assert_eq!(format!("{err}"), "invalid IP address format");
460 }
461}