1use std::collections::BTreeSet;
2use std::net::TcpListener;
3
4use crate::branch::BranchInstance;
5use crate::error::{NewgitError, Result};
6
7pub fn used_ports(branches: &[BranchInstance]) -> BTreeSet<u16> {
10 branches
11 .iter()
12 .flat_map(|branch| branch.resources.values())
13 .flat_map(|binding| binding.resolved_ports.values().copied())
14 .collect()
15}
16
17pub fn allocate(start: u16, used: &mut BTreeSet<u16>) -> Result<u16> {
21 let mut candidate = start;
22 loop {
23 if !used.contains(&candidate) && bindable(candidate) {
24 used.insert(candidate);
25 return Ok(candidate);
26 }
27 candidate = candidate.checked_add(1).ok_or_else(|| {
28 NewgitError::Unsupported(format!("no free port found scanning up from {start}"))
29 })?;
30 if candidate - start > 1000 {
31 return Err(NewgitError::Unsupported(format!(
32 "no free port found within 1000 of {start}"
33 )));
34 }
35 }
36}
37
38fn bindable(port: u16) -> bool {
39 TcpListener::bind(("127.0.0.1", port)).is_ok()
40}