use crate::{AddressEndpoint, BindEndpoint, BindOptions, CompositeEndpoint, ConnectEndpoint};
use base_scheduler::{
AddressHints, AddressInfoCollection, AddressInfoView, AddressResolution, AddressService,
Cancellation, OperationResult, SocketBind, SocketClose, SocketConnect, SocketDomain,
SocketListen, SocketOpen, SocketProtocol, SocketType,
};
use std::fmt;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HostEndpoint {
hostname: Option<String>,
port: u16,
domain: Option<SocketDomain>,
}
impl HostEndpoint {
pub fn new(hostname: impl Into<String>, port: u16) -> Self {
Self {
hostname: Some(hostname.into()),
port,
domain: None,
}
}
pub const fn wildcard(port: u16) -> Self {
Self {
hostname: None,
port,
domain: None,
}
}
pub fn with_domain(mut self, domain: SocketDomain) -> Self {
self.domain = Some(domain);
self
}
pub fn hostname(&self) -> Option<&str> {
self.hostname.as_deref()
}
pub const fn port(&self) -> u16 {
self.port
}
pub async fn resolve<S: AddressResolution + ?Sized>(
&self,
scheduler: &S,
passive: bool,
cancellation: &Cancellation,
) -> OperationResult<Vec<AddressEndpoint>> {
cancellation.check()?;
let addresses = scheduler
.address_resolve(
self.hostname(),
AddressService::Port(self.port),
AddressHints {
domain: self.domain,
socket_type: Some(SocketType::Stream),
protocol: Some(SocketProtocol::Tcp),
passive,
..AddressHints::default()
},
cancellation,
)
.await?;
Ok(addresses
.entries()
.map(|entry| AddressEndpoint::new(entry.address()))
.collect())
}
}
impl fmt::Display for HostEndpoint {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let hostname = self.hostname().unwrap_or("*");
if hostname.contains(':') {
write!(formatter, "tcp://[{hostname}]:{}", self.port)
} else {
write!(formatter, "tcp://{hostname}:{}", self.port)
}
}
}
impl<S> ConnectEndpoint<S> for HostEndpoint
where
S: AddressResolution + SocketOpen + SocketConnect + SocketClose + ?Sized,
{
type Connection = S::Socket;
async fn connect(
&self,
scheduler: &S,
cancellation: &Cancellation,
) -> OperationResult<S::Socket> {
CompositeEndpoint::new(self.resolve(scheduler, false, cancellation).await?)
.connect(scheduler, cancellation)
.await
}
}
impl<S> BindEndpoint<S> for HostEndpoint
where
S: AddressResolution + SocketOpen + SocketBind + SocketListen + SocketClose + ?Sized,
{
type Listener = S::Socket;
async fn bind(
&self,
scheduler: &S,
options: BindOptions,
cancellation: &Cancellation,
) -> OperationResult<Vec<S::Socket>> {
CompositeEndpoint::new(self.resolve(scheduler, true, cancellation).await?)
.bind(scheduler, options, cancellation)
.await
}
}