ip/
lib.rs

1//! Types and traits for working with IP addresses and prefixes generically
2//! over address families.
3//!
4//! The IP address types in [`std::net`] do not share any common trait that
5//! expresses "this thing is an IP address".
6//!
7//! This limitation makes writing code that deals with IP addresses in an
8//! address-family-independent way unnecessarily difficult.
9//!
10//! This crate provides a collection of types that seek to be compatible
11//! with the address types from [`std::net`] and prefix types from the
12//! popular [`ipnet`] crate, but which are generic over address-families.
13//!
14//! For example:
15//!
16//! ``` rust
17//! use ip::{Address, Afi, Error, Ipv4, Ipv6, Prefix};
18//!
19//! struct RibEntry<A: Afi> {
20//!     prefix: Prefix<A>,
21//!     next_hop: Address<A>,
22//! }
23//!
24//! impl<A: Afi> RibEntry<A> {
25//!     fn get_next_hop(&self, addr: Address<A>) -> Option<Address<A>> {
26//!         (self.prefix >= addr).then(|| self.next_hop)
27//!     }
28//! }
29//!
30//! fn main() -> Result<(), Error> {
31//!     let v4: RibEntry<Ipv4> = RibEntry {
32//!         prefix: "192.0.2.0/24".parse()?,
33//!         next_hop: "198.51.100.1".parse()?,
34//!     };
35//!
36//!     let v6: RibEntry<Ipv6> = RibEntry {
37//!         prefix: "2001:db8::/48".parse()?,
38//!         next_hop: "2001:db8:f00::1".parse()?,
39//!     };
40//!
41//!     assert_eq!(
42//!         v4.get_next_hop("192.0.2.127".parse()?),
43//!         Some("198.51.100.1".parse()?)
44//!     );
45//!     assert_eq!(v6.get_next_hop("2001:db8:ffff::ffff".parse()?), None);
46//!
47//!     Ok(())
48//! }
49//! ```
50//!
51//! ## Orientation
52//!
53//! Names such as `Address`, `Interface`, `Prefix` or `Afi` are re-used in
54//! various different modules within the crate.
55//! For example `Address` is used to name:
56//! - the type alias [`ip::Address<A>`][crate::Address]
57//! - the types [`ip::concrete::Address<A>`][crate::concrete::Address] and
58//!   [`ip::any::Address`][crate::any::Address]
59//! - the trait [`ip::traits::Address`][crate::traits::Address]
60//!
61//! This can make understanding which item a given name is referring to
62//! difficult without understanding the crate layout.
63//!
64//! ### Address-families
65//!
66//! The IP address-families `ipv4` and `ipv6` are represented in the type
67//! system by the zero-sized types [`concrete::Ipv4`] and [`concrete::Ipv6`].
68//!
69//! These "concrete" address-families implement [`traits::Afi`], which in turn
70//! bounds the generic parameter of the items exported by the [`concrete`]
71//! module, such as [`concrete::Address<A>`] and [`concrete::Prefix<A>`].
72//!
73//! Conversely, the [`any`] module exports a collection of `enum`s with
74//! variants corresponding to the two concrete address families, with each
75//! variant containing the corresponding `concrete::*` item.
76//!
77//! ### Address-family classes
78//!
79//! Usually a given use-case will call for *either* processing objects of a
80//! single known (at compile time) address-family or objects that may be of
81//! either address-family, as in the following:
82//!
83//! ``` rust
84//! use ip::{any, concrete, Afi, Ipv4, Ipv6};
85//!
86//! // `x` and `y` must be the same address-family
87//! fn longer_concrete<A: Afi>(
88//!     x: concrete::Prefix<A>,
89//!     y: concrete::Prefix<A>,
90//! ) -> concrete::Prefix<A> {
91//!     if x.length() > y.length() {
92//!         x
93//!     } else {
94//!         y
95//!     }
96//! }
97//!
98//! // `x` and `y` may be of different address families, so may not be
99//! // comparable
100//! fn longer_any(x: any::Prefix, y: any::Prefix) -> Option<any::Prefix> {
101//!     match (x, y) {
102//!         (any::Prefix::Ipv4(x), any::Prefix::Ipv4(y)) => Some(longer_concrete(x, y).into()),
103//!         (any::Prefix::Ipv6(x), any::Prefix::Ipv6(y)) => Some(longer_concrete(x, y).into()),
104//!         _ => None,
105//!     }
106//! }
107//!
108//! let x4: concrete::Prefix<Ipv4> = "192.0.2.0/24".parse().unwrap();
109//! let y4: concrete::Prefix<Ipv4> = "203.0.113.128/25".parse().unwrap();
110//!
111//! let x6: concrete::Prefix<Ipv6> = "2001:db8:f00::/48".parse().unwrap();
112//! let y6: concrete::Prefix<Ipv6> = "2001:db8::/32".parse().unwrap();
113//!
114//! assert_eq!(longer_concrete(x4, y4), y4);
115//! assert_eq!(longer_concrete(x6, y6), x6);
116//!
117//! assert_eq!(longer_any(x4.into(), y4.into()), Some(y4.into()));
118//! assert_eq!(longer_any(x4.into(), y6.into()), None);
119//! ```
120//!
121//! Occassionally, however, one may need a data structure that may
122//! sometimes contain a mix of address-families, but at other times must
123//! contain only a single address-family.
124//!
125//! To deal with such a requirement, [`traits::AfiClass`] provides
126//! further generalisation to avoid choosing between items from [`any`]
127//! or [`concrete`], by defining a type-level mapping from an
128//! "address-family class" to its associated type for `Address`, `Prefix`, etc.
129//!
130//! [`AfiClass`] is implemented for each of [`Ipv4`] and [`Ipv6`]. In this
131//! context [`Ipv4`]/[`Ipv6`] can be conceptually considered to be the singleton
132//! classes of address-families `{ ipv4 }` and `{ ipv6 }`.
133//!
134//! Additionally, the [`any::Any`] type implements [`AfiClass`], providing
135//! type-level mappings to the items of the [`any`] module. [`Any`] can be
136//! thought of as the class `{ ipv4, ipv6 }`.
137//!
138//! Various type aliases are defined at the crate root to provide easy
139//! access to this mapping. In general, it is easier and clearer to use
140//! [`Address<Ipv4>`] or [`Address<Any>`] than [`concrete::Address<Ipv4>`] or
141//! [`any::Address`].
142//!
143//! #### Example
144//!
145//! ``` rust
146//! use ip::{Address, Afi, AfiClass, Any, Ipv4};
147//!
148//! #[derive(Debug, PartialEq)]
149//! struct Foo<A: AfiClass> {
150//!     addr: Address<A>,
151//! }
152//!
153//! impl<A: AfiClass> Foo<A> {
154//!     fn new(addr: Address<A>) -> Self {
155//!         Self { addr }
156//!     }
157//!
158//!     fn into_concrete<C>(self) -> Option<Foo<C>>
159//!     where
160//!         C: Afi,
161//!         Address<C>: TryFrom<Address<A>>,
162//!     {
163//!         self.addr.try_into().map(Foo::new).ok()
164//!     }
165//! }
166//!
167//! let anys: Vec<Foo<Any>> = vec![
168//!     Foo {
169//!         addr: Address::<Any>::Ipv4("192.0.2.1".parse().unwrap()),
170//!     },
171//!     Foo {
172//!         addr: Address::<Any>::Ipv6("2001:db8::1".parse().unwrap()),
173//!     },
174//!     Foo {
175//!         addr: Address::<Any>::Ipv4("198.51.100.1".parse().unwrap()),
176//!     },
177//! ];
178//!
179//! let filtered: Vec<Foo<Ipv4>> = vec![
180//!     Foo {
181//!         addr: "192.0.2.1".parse().unwrap(),
182//!     },
183//!     Foo {
184//!         addr: "198.51.100.1".parse().unwrap(),
185//!     },
186//! ];
187//!
188//! assert_eq!(
189//!     anys.into_iter()
190//!         .filter_map(Foo::into_concrete)
191//!         .collect::<Vec<Foo<Ipv4>>>(),
192//!     filtered
193//! );
194//! ```
195#![doc(html_root_url = "https://docs.rs/generic-ip/0.1.1")]
196// clippy lints
197#![warn(clippy::pedantic)]
198#![warn(clippy::cargo)]
199#![warn(clippy::nursery)]
200#![allow(clippy::redundant_pub_crate)]
201// rustc lints
202#![allow(box_pointers)]
203#![warn(absolute_paths_not_starting_with_crate)]
204#![warn(deprecated_in_future)]
205#![warn(elided_lifetimes_in_paths)]
206#![warn(explicit_outlives_requirements)]
207#![warn(keyword_idents)]
208#![warn(macro_use_extern_crate)]
209#![warn(meta_variable_misuse)]
210#![warn(missing_abi)]
211#![warn(missing_copy_implementations)]
212#![warn(missing_debug_implementations)]
213#![warn(missing_docs)]
214#![warn(non_ascii_idents)]
215#![warn(noop_method_call)]
216#![warn(pointer_structural_match)]
217#![warn(rust_2021_incompatible_closure_captures)]
218#![warn(rust_2021_incompatible_or_patterns)]
219#![warn(rust_2021_prefixes_incompatible_syntax)]
220#![warn(rust_2021_prelude_collisions)]
221#![warn(single_use_lifetimes)]
222#![warn(trivial_casts)]
223#![warn(trivial_numeric_casts)]
224#![warn(unreachable_pub)]
225#![warn(unsafe_code)]
226#![warn(unsafe_op_in_unsafe_fn)]
227#![warn(unstable_features)]
228#![warn(unused_crate_dependencies)]
229#![warn(unused_extern_crates)]
230#![warn(unused_import_braces)]
231#![warn(unused_lifetimes)]
232#![warn(unused_qualifications)]
233#![warn(unused_results)]
234#![warn(variant_size_differences)]
235// docs.rs build config
236#![cfg_attr(docsrs, feature(doc_auto_cfg))]
237// no_std support
238#![no_std]
239#[cfg(feature = "std")]
240extern crate std;
241// silence unused dev-dependency warnings
242#[cfg(test)]
243mod deps {
244    use criterion as _;
245    use itertools as _;
246    use utils as _;
247    use version_sync as _;
248}
249
250/// Types for working with IP objects of either address family.
251pub mod any;
252pub use self::any::Any;
253
254/// Types for working with IP objects of a specific address family.
255pub mod concrete;
256pub use self::concrete::{Ipv4, Ipv6};
257
258/// Traits describing address family independent interfaces for IP objects.
259pub mod traits;
260pub use self::traits::{Afi, AfiClass};
261
262/// Error types.
263pub mod error;
264pub use self::error::Error;
265
266/// IP address formatting traits
267mod fmt;
268
269/// Parsers for IP object textual representations.
270mod parser;
271
272/// Convenience alias to name types implementing [`traits::Address`].
273pub type Address<A> = <A as AfiClass>::Address;
274
275/// Convenience alias to name types implementing [`traits::Interface`].
276pub type Interface<A> = <A as AfiClass>::Interface;
277
278/// Convenience alias to name types implementing [`traits::Prefix`].
279pub type Prefix<A> = <A as AfiClass>::Prefix;
280
281/// Convenience alias to name types implementing [`traits::PrefixLength`].
282pub type PrefixLength<A> = <A as AfiClass>::PrefixLength;
283
284/// Convenience alias to name types implementing [`traits::PrefixRange`].
285pub type PrefixRange<A> = <A as AfiClass>::PrefixRange;
286
287/// Convenience alias to name types implementing [`traits::PrefixSet`].
288#[cfg(feature = "std")]
289pub type PrefixSet<A> = <A as AfiClass>::PrefixSet;
290
291/// Convenience alias to name types implementing [`traits::Netmask`].
292pub type Netmask<A> = <A as AfiClass>::Netmask;
293
294/// Convenience alias to name types implementing [`traits::Hostmask`].
295pub type Hostmask<A> = <A as AfiClass>::Hostmask;
296
297/// Convenience alias to name types implementing [`traits::Bitmask`].
298pub type Bitmask<A> = <A as AfiClass>::Bitmask;
299
300#[cfg(test)]
301mod tests;