use crate::zeromq::{Socket, ZmqError};
use super::endpoint::{host_is_literal, parse_tcp_host_port, resolve_tcp_connect};
use super::error::DnsError;
#[derive(Debug, thiserror::Error)]
pub enum ZmqConnectError {
#[error(transparent)]
Dns(#[from] DnsError),
#[error(transparent)]
Zmq(#[from] ZmqError),
}
async fn resolve_endpoint(endpoint: &str) -> Result<String, DnsError> {
if endpoint.starts_with("tcp://") {
let (host, _) = parse_tcp_host_port(endpoint)?;
if host_is_literal(&host) {
return Ok(endpoint.to_string());
}
return resolve_tcp_connect(endpoint).await;
}
Ok(endpoint.to_string())
}
pub async fn connect_resolved<S: Socket>(
socket: &mut S,
endpoint: &str,
) -> Result<(), ZmqConnectError> {
let resolved = resolve_endpoint(endpoint).await?;
socket.connect(&resolved).await?;
Ok(())
}
pub async fn connect_resolved_first<S: Socket>(
socket: &mut S,
endpoint: &str,
) -> Result<String, ZmqConnectError> {
let candidates = if endpoint.starts_with("tcp://") {
let (host, _) = parse_tcp_host_port(endpoint).map_err(DnsError::Tcp)?;
if host_is_literal(&host) {
vec![endpoint.to_string()]
} else {
super::resolve_tcp_connect_all(endpoint).await?
}
} else {
vec![endpoint.to_string()]
};
if candidates.is_empty() {
return Err(DnsError::Tcp(super::error::TcpEndpointError::NoAddresses).into());
}
let mut last_err = None;
for candidate in candidates {
match socket.connect(&candidate).await {
Ok(()) => return Ok(candidate),
Err(e) => last_err = Some(e),
}
}
Err(last_err.expect("non-empty candidates").into())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn resolve_endpoint_leaves_literal_unchanged() {
let out = resolve_endpoint("tcp://10.0.0.5:5555").await.unwrap();
assert_eq!(out, "tcp://10.0.0.5:5555");
}
#[tokio::test]
async fn resolve_endpoint_passes_ipc_through() {
let out = resolve_endpoint("ipc:///run/nautalid/bus.sock")
.await
.unwrap();
assert_eq!(out, "ipc:///run/nautalid/bus.sock");
}
}