use std::fmt::Debug;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use infinity_pool::{BlindPool, BlindPooledMut, define_pooled_dyn_cast};
use crate::Service;
pub trait DynamicServiceExt<In, Out>: Sized {
fn into_dynamic(self) -> DynamicService<In, Out>;
}
impl<In: Send + 'static, Out: Send + 'static, T> DynamicServiceExt<In, Out> for T
where
T: Service<In, Out = Out> + 'static,
{
fn into_dynamic(self) -> DynamicService<In, Out> {
DynamicService::new(self)
}
}
pub struct DynamicService<In, Out> {
exec: Arc<dyn Fn(In) -> BlindPooledMut<dyn SendFuture<Out>> + Send + Sync>,
}
pub(crate) trait SendFuture<Out>: Future<Output = Out> + Send {}
impl<Out: Send + 'static, F: Future<Output = Out> + Send> SendFuture<Out> for F {}
define_pooled_dyn_cast!(SendFuture<Out>);
impl<In: Send + 'static, Out: Send + 'static> DynamicService<In, Out> {
pub(crate) fn new<T>(strategy: T) -> Self
where
T: Service<In, Out = Out> + Send + Sync + 'static,
{
let pool = BlindPool::new();
let service = Arc::new(strategy);
let exec = move |input: In| {
let cloned = Arc::clone(&service);
let fut = async move { cloned.execute(input).await };
pool.insert(fut).cast_send_future()
};
Self { exec: Arc::new(exec) }
}
}
impl<In: Send, Out: Send> Service<In> for DynamicService<In, Out> {
type Out = Out;
fn execute(&self, input: In) -> impl Future<Output = Self::Out> + Send {
ServiceFuture {
handle: self.exec.as_ref()(input),
}
}
}
struct ServiceFuture<Out> {
handle: BlindPooledMut<dyn SendFuture<Out>>,
}
impl<Out> Future for ServiceFuture<Out> {
type Output = Out;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.get_mut().handle.as_pin_mut().poll(cx)
}
}
impl<In, Out> Debug for DynamicService<In, Out> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DynamicService").finish()
}
}
impl<In, Out> Clone for DynamicService<In, Out> {
fn clone(&self) -> Self {
Self {
exec: Arc::clone(&self.exec),
}
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use std::sync::Mutex;
use futures::executor::block_on;
use static_assertions::assert_impl_all;
use super::*;
use crate::Execute;
#[test]
fn assert_types() {
assert_impl_all!(DynamicService<(), ()>: Send, Sync, Clone, Debug);
assert_impl_all!(DynamicService<Mutex<()>, Mutex<()>>: Send, Sync, Clone, Debug);
}
#[test]
fn into_dynamic() {
let dynamic_service: DynamicService<i32, i32> = Execute::new(|v| async move { v }).into_dynamic();
assert_eq!(block_on(dynamic_service.execute(42)), 42);
}
#[test]
fn clone_and_debug() {
let svc: DynamicService<i32, i32> = Execute::new(|v| async move { v }).into_dynamic();
let cloned = svc.clone();
assert_eq!(block_on(cloned.execute(1)), 1);
assert_eq!(format!("{svc:?}"), "DynamicService");
}
}