use std::fmt;
use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::{TcpListener, TcpStream};
pub trait PeerStream: AsyncRead + AsyncWrite + Send + Unpin + 'static {}
impl<T: AsyncRead + AsyncWrite + Send + Unpin + 'static> PeerStream for T {}
pub type BoxStream = Pin<Box<dyn PeerStream>>;
pub type AcceptFuture<'a> =
Pin<Box<dyn Future<Output = Result<BoxStream, NetworkError>> + Send + 'a>>;
pub type BindFuture<'a> = Pin<
Box<dyn Future<Output = Result<(SocketAddr, Box<dyn Acceptor>), NetworkError>> + Send + 'a>,
>;
pub type ConnectFuture<'a> =
Pin<Box<dyn Future<Output = Result<BoxStream, NetworkError>> + Send + 'a>>;
#[derive(Debug)]
pub struct NetworkError(pub String);
impl fmt::Display for NetworkError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "network error: {}", self.0)
}
}
impl std::error::Error for NetworkError {}
impl From<std::io::Error> for NetworkError {
fn from(e: std::io::Error) -> Self {
Self(e.to_string())
}
}
pub trait Acceptor: Send + 'static {
fn accept(&mut self) -> AcceptFuture<'_>;
}
pub trait Network: Send + Sync + 'static {
fn bind(&self, addr: SocketAddr) -> BindFuture<'_>;
fn connect(&self, addr: SocketAddr) -> ConnectFuture<'_>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct TcpNetwork;
struct TcpAcceptor {
listener: TcpListener,
}
impl Acceptor for TcpAcceptor {
fn accept(&mut self) -> AcceptFuture<'_> {
Box::pin(async move {
let (stream, _) = self.listener.accept().await?;
if let Err(e) = stream.set_nodelay(true) {
tracing::warn!("failed to set TCP_NODELAY on accepted peer: {e}");
}
let boxed: Box<dyn PeerStream> = Box::new(stream);
Ok(Pin::new(boxed))
})
}
}
impl Network for TcpNetwork {
fn bind(&self, addr: SocketAddr) -> BindFuture<'_> {
Box::pin(async move {
let listener = TcpListener::bind(addr).await?;
let bound = listener.local_addr()?;
Ok((
bound,
Box::new(TcpAcceptor { listener }) as Box<dyn Acceptor>,
))
})
}
fn connect(&self, addr: SocketAddr) -> ConnectFuture<'_> {
Box::pin(async move {
let stream = TcpStream::connect(addr).await?;
if let Err(e) = stream.set_nodelay(true) {
tracing::warn!("failed to set TCP_NODELAY on dialed peer: {e}");
}
let boxed: Box<dyn PeerStream> = Box::new(stream);
Ok(Pin::new(boxed))
})
}
}