#![allow(clippy::disallowed_types, reason = "traits require the `Arc` from std")]
mod executor_error;
mod no_std_runtime;
#[cfg(feature = "std")]
mod std_executor;
#[cfg(feature = "std")]
mod std_runtime;
#[cfg(feature = "tokio")]
mod tokio_executor;
use crate::net::{TcpListener, TcpStream};
pub use executor_error::ExecutorError;
pub use no_std_runtime::NoStdRuntime;
#[cfg(feature = "tokio")]
pub use tokio_executor::{TokioExecutor, TokioSpawnFutureFuture};
#[cfg(feature = "std")]
use {crate::net::ToSocketAddrs, core::net::SocketAddr};
#[cfg(feature = "std")]
pub use {
std_executor::{StdExecutor, StdSpawnFuture, StdSpawnLocalFuture},
std_runtime::{SpawnFuture, StdRuntime},
};
#[derive(Clone, Copy, Debug)]
pub enum ExecutorTy {
Std,
Tokio,
}
impl ExecutorTy {
#[inline]
#[must_use]
pub const fn is_std(&self) -> bool {
matches!(self, Self::Std)
}
}
pub trait Executor: Default {
const TY: ExecutorTy;
type LocalRuntime: Runtime;
type SpawnFuture<T>: Future<Output = crate::Result<T>>;
type SpawnLocalFuture<T>: Future<Output = crate::Result<T>>;
type TcpListener: TcpListener<TcpStream = Self::TcpStream>;
type TcpStream: TcpStream<Executor = Self>;
fn spawn<F>(&self, future: F) -> Self::SpawnFuture<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static;
fn spawn_local<F>(&self, future: F, lc: &Self::LocalRuntime) -> Self::SpawnLocalFuture<F::Output>
where
F: Future + 'static,
F::Output: 'static;
}
pub trait Runtime: Sized {
fn new() -> crate::Result<Self>;
fn block_on<F>(&self, future: F) -> F::Output
where
F: Future;
}
#[cfg(feature = "std")]
async fn tcp_listener_std<A, EX>(
addr: A,
executor: &EX,
_tcp_params: crate::net::TcpParams,
) -> crate::Result<std::net::TcpListener>
where
A: ToSocketAddrs,
EX: Executor,
{
let fun = async |socket_addr: SocketAddr| {
cfg_select! {
feature = "socket2" => {
let domain = if socket_addr.is_ipv4() {
socket2::Domain::IPV4
} else {
socket2::Domain::IPV6
};
let socket = socket2::Socket::new(domain, socket2::Type::STREAM, None)?;
if let Some(elem) = _tcp_params.reuse_address {
socket.set_reuse_address(elem)?;
}
#[cfg(not(any(
target_os = "cygwin",
target_os = "illumos",
target_os = "solaris",
target_os = "wasi"
)))]
if let Some(elem) = _tcp_params.reuse_port {
socket.set_reuse_port(elem)?;
}
socket.set_tcp_nodelay(_tcp_params.tcp_nodelay)?;
socket.bind(&socket_addr.into())?;
socket.listen(_tcp_params.listen)?;
Ok(std::net::TcpListener::from(socket))
},
_ => Ok(std::net::TcpListener::bind(socket_addr)?)
}
};
resolve_addrs(addr, executor, fun).await
}
#[cfg(feature = "std")]
async fn resolve_addrs<A, EX, T>(
addr: A,
executor: &EX,
mut cb: impl AsyncFnMut(SocketAddr) -> crate::Result<T>,
) -> crate::Result<T>
where
A: ToSocketAddrs,
EX: Executor,
{
let mut last_err = None;
for socket_addr in addr.to_socket_addrs(executor).await? {
match cb(socket_addr).await {
Ok(el) => return Ok(el),
Err(err) => last_err = Some(err),
}
}
Err(last_err.unwrap_or_else(|| ExecutorError::InvalidResolvedAddress.into()))
}