unique_port/
lib.rs

1#![crate_name = "unique_port"]
2
3use std::net::{Ipv4Addr, SocketAddrV4, TcpListener};
4use std::ops::Range;
5use std::sync::Mutex;
6
7use once_cell::sync::Lazy;
8
9static PORT_IDX: Lazy<Mutex<u16>> = Lazy::new(|| Mutex::new(1000));
10
11/// Generates a unique offset, from which `get_unique_free_port` will start to find free ports
12/// incrementally. The value is higher than 1000, and less than `u16::MAX - 1000`. It uses the full
13/// module path and the enclosed function name, so it's always the same for the scope of the same
14/// function.
15#[macro_export]
16macro_rules! generate_unique_start_port {
17    () => {{
18        use std::collections::hash_map::DefaultHasher;
19        use std::hash::{Hash, Hasher};
20
21        fn f() {}
22        fn type_name_of<T>(_: T) -> &'static str {
23            std::any::type_name::<T>()
24        }
25        let name = type_name_of(f);
26        let name = &name[..name.len() - 3];
27        let mut hasher = DefaultHasher::new();
28        name.hash(&mut hasher);
29        // we have offset of 1000, which is the starting port numnber, so we should move the whole
30        // offset and prevent it from overflowing u16::MAX
31        1000 + (hasher.finish() % ((u16::MAX - 1000) as u64)) as u16
32    }};
33}
34
35/// Sets the port number, from which `get_unique_free_port()` will start generating free ports
36/// incrementally.
37///
38/// # Examples
39///
40/// ```
41/// use unique_port;
42///
43/// // this may fail if port number 1042 is not free.
44///
45/// let pindex = 1042;
46///
47/// unique_port::set_port_index(pindex).unwrap();
48/// assert_eq!(pindex, unique_port::get_unique_free_port().unwrap());
49///
50/// unique_port::set_port_index(pindex).unwrap();
51/// assert_eq!(pindex, unique_port::get_unique_free_port().unwrap());
52///
53/// ```
54pub fn set_port_index(pindex: u16) -> Result<(), String> {
55    *PORT_IDX
56        .lock()
57        .map_err(|_| "Failed to aquire the lock".to_owned())? = pindex;
58
59    Ok(())
60}
61
62/// Returns a free unique local port. Every time a call to this function during one run should
63/// return a unique address.
64///
65/// # Examples
66/// ```
67/// use unique_port::get_unique_free_port;
68///
69/// let port_1 = get_unique_free_port().unwrap();
70/// let port_2 = get_unique_free_port().unwrap();
71/// assert_ne!(port_1, port_2);
72/// ```
73pub fn get_unique_free_port() -> Result<u16, String> {
74    let mut port_idx = PORT_IDX
75        .lock()
76        .map_err(|_| "Failed to aquire the lock".to_owned())?;
77    let result = get_free_port(*port_idx..u16::MAX);
78    if let Ok(port) = result {
79        *port_idx = port + 1;
80    }
81    result
82}
83
84/// Returns empty port from range. Can be not unique
85fn get_free_port(ports: Range<u16>) -> Result<u16, String> {
86    ports
87        .into_iter()
88        .find(|port| TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, *port)).is_ok())
89        .ok_or_else(|| "Failed to get empty port".to_owned())
90}