use std::time::{Duration, Instant};
use anyhow::{Result, bail};
pub(super) fn find_available_port(preferred: u16) -> Result<u16> {
find_available_port_excluding(preferred, &[])
}
pub(super) fn find_available_port_excluding(preferred: u16, excluded: &[u16]) -> Result<u16> {
if !excluded.contains(&preferred) && std::net::TcpListener::bind(("0.0.0.0", preferred)).is_ok() {
return Ok(preferred);
}
for port in 3000..3100 {
if port != preferred
&& !excluded.contains(&port)
&& std::net::TcpListener::bind(("0.0.0.0", port)).is_ok()
{
return Ok(port);
}
}
bail!("no available port found in range 3000-3099");
}
pub(super) fn preferred_vite_port(port: Option<u16>) -> u16 {
port.unwrap_or(5173)
}
pub(super) async fn wait_for_port(port: u16, timeout: Duration) -> Result<()> {
let deadline = Instant::now() + timeout;
loop {
if tokio::net::TcpStream::connect(("::1", port)).await.is_ok()
|| tokio::net::TcpStream::connect(("127.0.0.1", port)).await.is_ok()
{
return Ok(());
}
if Instant::now() >= deadline {
bail!("timed out waiting for port {port} to become ready");
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
}