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}