use std::net::{TcpStream, ToSocketAddrs};
use std::time::Duration;
use crate::rendezvous::call::Roving;
const DIRECT: Duration = Duration::from_secs(10);
pub(super) fn climb(
address: &str,
roving: Option<&mut Roving>,
) -> Result<(TcpStream, bool), String> {
let direct = direct(address);
let Some(roving) = roving else {
return direct.map(|tcp| (tcp, false));
};
let refusal = match direct {
Ok(tcp) => return Ok((tcp, false)),
Err(refusal) => refusal,
};
if let Some(tcp) = roving.repunch() {
return Ok((tcp, true));
}
match roving.rendezvous() {
Ok(tcp) => Ok((tcp, true)),
Err(why) => Err(format!("{refusal}; rendezvous: {why}")),
}
}
fn direct(address: &str) -> Result<TcpStream, String> {
let resolved = address
.to_socket_addrs()
.map_err(|e| format!("connect {address}: {e}"))?;
let mut last = None;
for at in resolved {
match TcpStream::connect_timeout(&at, DIRECT) {
Ok(tcp) => return Ok(tcp),
Err(e) => last = Some(e),
}
}
Err(format!(
"connect {address}: {}",
last.map_or_else(|| "no address resolved".to_owned(), |e| e.to_string())
))
}