//! Define a [`CompoundRuntime`] part that can be built from several component //! pieces. use std::{net::SocketAddr, sync::Arc, time::Duration}; use crate::traits::*; use async_trait::async_trait; use educe::Educe; use futures::{future::FutureObj, task::Spawn}; use std::io::Result as IoResult; use std::time::{Instant, SystemTime}; /// A runtime made of several parts, each of which implements one trait-group. /// /// The `SpawnR` component should implements [`Spawn`] and [`BlockOn`]; /// the `SleepR` component should implement [`SleepProvider`]; the `TcpR` /// component should implement [`TcpProvider`]; and the `TlsR` component should /// implement [`TlsProvider`]. /// /// You can use this structure to create new runtimes in two ways: either by /// overriding a single part of an existing runtime, or by building an entirely /// new runtime from pieces. #[derive(Educe)] #[educe(Clone)] // #[derive(Clone)] wrongly infers Clone bounds on the generic parameters pub struct CompoundRuntime { /// The actual collection of Runtime objects. /// /// We wrap this in an Arc rather than requiring that each item implement /// Clone, though we could change our minds later on. inner: Arc>, } /// A collection of objects implementing that traits that make up a [`Runtime`] struct Inner { /// A `Spawn` and `BlockOn` implementation. spawn: SpawnR, /// A `SleepProvider` implementation. sleep: SleepR, /// A `TcpProvider` implementation tcp: TcpR, /// A `TcpProvider` implementation. tls: TlsR, /// A `UdpProvider` implementation udp: UdpR, } impl CompoundRuntime { /// Construct a new CompoundRuntime from its components. pub fn new(spawn: SpawnR, sleep: SleepR, tcp: TcpR, tls: TlsR, udp: UdpR) -> Self { CompoundRuntime { inner: Arc::new(Inner { spawn, sleep, tcp, tls, udp, }), } } } impl Spawn for CompoundRuntime where SpawnR: Spawn, { #[inline] fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), futures::task::SpawnError> { self.inner.spawn.spawn_obj(future) } } impl BlockOn for CompoundRuntime where SpawnR: BlockOn, SleepR: Clone + Send + Sync + 'static, TcpR: Clone + Send + Sync + 'static, TlsR: Clone + Send + Sync + 'static, UdpR: Clone + Send + Sync + 'static, { #[inline] fn block_on(&self, future: F) -> F::Output { self.inner.spawn.block_on(future) } } impl SleepProvider for CompoundRuntime where SleepR: SleepProvider, SpawnR: Clone + Send + Sync + 'static, TcpR: Clone + Send + Sync + 'static, TlsR: Clone + Send + Sync + 'static, UdpR: Clone + Send + Sync + 'static, { type SleepFuture = SleepR::SleepFuture; #[inline] fn sleep(&self, duration: Duration) -> Self::SleepFuture { self.inner.sleep.sleep(duration) } #[inline] fn now(&self) -> Instant { self.inner.sleep.now() } #[inline] fn wallclock(&self) -> SystemTime { self.inner.sleep.wallclock() } } #[async_trait] impl TcpProvider for CompoundRuntime where TcpR: TcpProvider, SpawnR: Send + Sync + 'static, SleepR: Send + Sync + 'static, TcpR: Send + Sync + 'static, TlsR: Send + Sync + 'static, UdpR: Send + Sync + 'static, { type TcpStream = TcpR::TcpStream; type TcpListener = TcpR::TcpListener; #[inline] async fn connect(&self, addr: &SocketAddr) -> IoResult { self.inner.tcp.connect(addr).await } #[inline] async fn listen(&self, addr: &SocketAddr) -> IoResult { self.inner.tcp.listen(addr).await } } impl TlsProvider for CompoundRuntime where TcpR: TcpProvider, TlsR: TlsProvider, SleepR: Clone + Send + Sync + 'static, SpawnR: Clone + Send + Sync + 'static, UdpR: Clone + Send + Sync + 'static, { type Connector = TlsR::Connector; type TlsStream = TlsR::TlsStream; #[inline] fn tls_connector(&self) -> Self::Connector { self.inner.tls.tls_connector() } } impl std::fmt::Debug for CompoundRuntime { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CompoundRuntime").finish_non_exhaustive() } } #[async_trait] impl UdpProvider for CompoundRuntime where UdpR: UdpProvider, SpawnR: Send + Sync + 'static, SleepR: Send + Sync + 'static, TcpR: Send + Sync + 'static, TlsR: Send + Sync + 'static, UdpR: Send + Sync + 'static, { type UdpSocket = UdpR::UdpSocket; #[inline] async fn bind(&self, addr: &SocketAddr) -> IoResult { self.inner.udp.bind(addr).await } }