Skip to main content

rush_sync_server/server/utils/
port.rs

1use crate::core::config::Config;
2use crate::core::prelude::*;
3
4pub enum PortStatus {
5    Available,
6    OccupiedByUs,
7    OccupiedByOther,
8}
9
10pub fn check_port_status(port: u16, bind_address: &str) -> PortStatus {
11    // free?
12    if is_port_available(port, bind_address) {
13        return PortStatus::Available;
14    }
15
16    // occupied - check if by us
17    let ctx = crate::server::shared::get_shared_context();
18    if let Ok(servers) = ctx.servers.read() {
19        if servers.values().any(|s| s.port == port) {
20            return PortStatus::OccupiedByUs;
21        }
22    }
23
24    PortStatus::OccupiedByOther
25}
26
27pub fn is_port_available(port: u16, bind_address: &str) -> bool {
28    std::net::TcpListener::bind((bind_address, port))
29        .map(|l| {
30            drop(l);
31            true
32        })
33        .unwrap_or(false)
34}
35
36pub fn find_next_available_port(config: &Config) -> Result<u16> {
37    let ctx = crate::server::shared::get_shared_context();
38    let servers = crate::core::helpers::read_lock(&ctx.servers, "servers")?;
39    let mut used_ports: Vec<u16> = servers.values().map(|s| s.port).collect();
40    used_ports.sort();
41
42    let mut candidate_port = config.server.port_range_start;
43    let max_port = config.server.port_range_end;
44
45    loop {
46        if candidate_port > max_port {
47            return Err(AppError::Validation(format!(
48                "No available ports in range {}-{}",
49                config.server.port_range_start, config.server.port_range_end
50            )));
51        }
52
53        if !used_ports.contains(&candidate_port)
54            && is_port_available(candidate_port, &config.server.bind_address)
55        {
56            return Ok(candidate_port);
57        }
58
59        candidate_port += 1;
60    }
61}