1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#![crate_name = "unique_port"]

use std::net::{Ipv4Addr, SocketAddrV4, TcpListener};
use std::ops::Range;
use std::sync::Mutex;

use once_cell::sync::Lazy;

static PORT_IDX: Lazy<Mutex<u16>> = Lazy::new(|| Mutex::new(1000));

/// Generates a unique offset, from which `get_unique_free_port` will start to find free ports
/// incrementally. The value is higher than 1000, and less than `u16::MAX - 1000`. It uses the full
/// module path and the enclosed function name, so it's always the same for the scope of the same
/// function.
#[macro_export]
macro_rules! generate_unique_start_port {
    () => {{
        use std::collections::hash_map::DefaultHasher;
        use std::hash::{Hash, Hasher};

        fn f() {}
        fn type_name_of<T>(_: T) -> &'static str {
            std::any::type_name::<T>()
        }
        let name = type_name_of(f);
        let name = &name[..name.len() - 3];
        let mut hasher = DefaultHasher::new();
        name.hash(&mut hasher);
        // we have offset of 1000, which is the starting port numnber, so we should move the whole
        // offset and prevent it from overflowing u16::MAX
        1000 + (hasher.finish() % ((u16::MAX - 1000) as u64)) as u16
    }};
}

/// Sets the port number, from which `get_unique_free_port()` will start generating free ports
/// incrementally.
///
/// # Examples
///
/// ```
/// use unique_port;
///
/// // this may fail if port number 1042 is not free.
///
/// let pindex = 1042;
///
/// unique_port::set_port_index(pindex).unwrap();
/// assert_eq!(pindex, unique_port::get_unique_free_port().unwrap());
///
/// unique_port::set_port_index(pindex).unwrap();
/// assert_eq!(pindex, unique_port::get_unique_free_port().unwrap());
///
/// ```
pub fn set_port_index(pindex: u16) -> Result<(), String> {
    *PORT_IDX
        .lock()
        .map_err(|_| "Failed to aquire the lock".to_owned())? = pindex;

    Ok(())
}

/// Returns a free unique local port. Every time a call to this function during one run should
/// return a unique address.
///
/// # Examples
/// ```
/// use unique_port::get_unique_free_port;
///
/// let port_1 = get_unique_free_port().unwrap();
/// let port_2 = get_unique_free_port().unwrap();
/// assert_ne!(port_1, port_2);
/// ```
pub fn get_unique_free_port() -> Result<u16, String> {
    let mut port_idx = PORT_IDX
        .lock()
        .map_err(|_| "Failed to aquire the lock".to_owned())?;
    let result = get_free_port(*port_idx..u16::MAX);
    if let Ok(port) = result {
        *port_idx = port + 1;
    }
    result
}

/// Returns empty port from range. Can be not unique
fn get_free_port(ports: Range<u16>) -> Result<u16, String> {
    ports
        .into_iter()
        .find(|port| TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, *port)).is_ok())
        .ok_or_else(|| "Failed to get empty port".to_owned())
}