service_async/
boxed.rs

1use std::{
2    any::{Any, TypeId},
3    future::Future,
4    marker::PhantomData,
5    pin::Pin,
6};
7
8use crate::{AsyncMakeService, MakeService, Service};
9/// A type-erased wrapper for services, enabling dynamic dispatch.
10///  `BoxedService` allows for storing and using services of different types
11/// through a common interface.
12pub struct BoxedService<Request, Response, E> {
13    svc: *const (),
14    type_id: TypeId,
15    vtable: ServiceVtable<Request, Response, E>,
16}
17
18impl<Request, Response, E> BoxedService<Request, Response, E> {
19    pub fn new<S>(s: S) -> Self
20    where
21        S: Service<Request, Response = Response, Error = E> + 'static,
22        Request: 'static,
23    {
24        let type_id = s.type_id();
25        let svc = Box::into_raw(Box::new(s)) as *const ();
26        BoxedService {
27            svc,
28            type_id,
29            vtable: ServiceVtable {
30                call: call::<Request, S>,
31                drop: drop::<S>,
32            },
33        }
34    }
35
36    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
37        let t = TypeId::of::<T>();
38        if self.type_id == t {
39            Some(unsafe { self.downcast_ref_unchecked() })
40        } else {
41            None
42        }
43    }
44
45    /// # Safety
46    /// If you are sure the inner type is T, you can downcast it.
47    pub unsafe fn downcast_ref_unchecked<T: Any>(&self) -> &T {
48        &*(self.svc as *const T)
49    }
50}
51
52impl<Request, Response, E> Drop for BoxedService<Request, Response, E> {
53    #[inline]
54    fn drop(&mut self) {
55        unsafe { (self.vtable.drop)(self.svc) };
56    }
57}
58
59impl<Request, Response, E> Service<Request> for BoxedService<Request, Response, E> {
60    type Response = Response;
61    type Error = E;
62
63    #[inline]
64    fn call(&self, req: Request) -> impl Future<Output = Result<Self::Response, Self::Error>> {
65        unsafe { (self.vtable.call)(self.svc, req) }
66    }
67}
68
69/// Trait for converting a service into a boxed service.
70///
71/// This trait provides a method to convert any compatible service
72/// into a `BoxedService`, facilitating type erasure.
73pub trait BoxService<Request, Response, E> {
74    fn into_boxed(self) -> BoxedService<Request, Response, E>;
75}
76
77impl<T, Request, Response, E> BoxService<Request, Response, E> for T
78where
79    T: Service<Request, Response = Response, Error = E> + 'static,
80    Request: 'static,
81{
82    fn into_boxed(self) -> BoxedService<Request, Response, E> {
83        BoxedService::new(self)
84    }
85}
86
87type LocalStaticBoxedFuture<T, E> = Pin<Box<dyn Future<Output = Result<T, E>> + 'static>>;
88
89struct ServiceVtable<T, U, E> {
90    call: unsafe fn(raw: *const (), req: T) -> LocalStaticBoxedFuture<U, E>,
91    drop: unsafe fn(raw: *const ()),
92}
93
94unsafe fn call<R, S>(svc: *const (), req: R) -> LocalStaticBoxedFuture<S::Response, S::Error>
95where
96    R: 'static,
97    S: Service<R> + 'static,
98{
99    let svc = &*svc.cast::<S>();
100    let fut = S::call(svc, req);
101    Box::pin(fut)
102}
103
104unsafe fn drop<S>(raw: *const ()) {
105    std::ptr::drop_in_place(raw as *mut S);
106}
107
108// A factory for creating boxed services.
109///
110/// `BoxServiceFactory` wraps a service factory and produces `BoxedService` instances,
111/// allowing for type erasure in service creation pipelines.
112pub struct BoxServiceFactory<F, Req> {
113    pub inner: F,
114    _marker: PhantomData<Req>,
115}
116
117unsafe impl<F: Send, Req> Send for BoxServiceFactory<F, Req> {}
118
119unsafe impl<F: Sync, Req> Sync for BoxServiceFactory<F, Req> {}
120
121impl<F, Req> BoxServiceFactory<F, Req> {
122    pub fn new(inner: F) -> Self {
123        BoxServiceFactory {
124            inner,
125            _marker: PhantomData,
126        }
127    }
128}
129
130impl<F, Req> MakeService for BoxServiceFactory<F, Req>
131where
132    F: MakeService,
133    F::Service: Service<Req> + 'static,
134    Req: 'static,
135{
136    type Service = BoxedService<
137        Req,
138        <F::Service as Service<Req>>::Response,
139        <F::Service as Service<Req>>::Error,
140    >;
141    type Error = F::Error;
142
143    fn make_via_ref(&self, old: Option<&Self::Service>) -> Result<Self::Service, Self::Error> {
144        let svc = match old {
145            Some(inner) => self.inner.make_via_ref(inner.downcast_ref())?,
146            None => self.inner.make()?,
147        };
148        Ok(svc.into_boxed())
149    }
150}
151
152impl<F, Req> AsyncMakeService for BoxServiceFactory<F, Req>
153where
154    F: AsyncMakeService,
155    F::Service: Service<Req> + 'static,
156    Req: 'static,
157{
158    type Service = BoxedService<
159        Req,
160        <F::Service as Service<Req>>::Response,
161        <F::Service as Service<Req>>::Error,
162    >;
163    type Error = F::Error;
164
165    async fn make_via_ref(
166        &self,
167        old: Option<&Self::Service>,
168    ) -> Result<Self::Service, Self::Error> {
169        let svc = match old {
170            Some(inner) => self.inner.make_via_ref(inner.downcast_ref()).await?,
171            None => self.inner.make().await?,
172        };
173        Ok(svc.into_boxed())
174    }
175}
176/// A type-erased wrapper for asynchronous service factories.
177///
178/// `BoxedAsyncMakeService` enables dynamic dispatch for async service factories,
179/// allowing for flexible composition of asynchronous service creation pipelines.
180pub struct BoxedAsyncMakeService<S, E> {
181    svc: *const (),
182    type_id: TypeId,
183    vtable: AsyncMakeServiceVtable<S, E>,
184}
185
186unsafe impl<S, E> Send for BoxedAsyncMakeService<S, E> {}
187unsafe impl<S, E> Sync for BoxedAsyncMakeService<S, E> {}
188
189impl<S, E> BoxedAsyncMakeService<S, E> {
190    pub fn new<AMS>(ams: AMS) -> Self
191    where
192        AMS: AsyncMakeService<Service = S, Error = E> + Send + Sync + 'static,
193        S: 'static,
194    {
195        let type_id = ams.type_id();
196        let svc = Box::into_raw(Box::new(ams)) as *const ();
197        BoxedAsyncMakeService {
198            svc,
199            type_id,
200            vtable: AsyncMakeServiceVtable {
201                make_via_ref: make_via_ref::<AMS, S, E>,
202                drop: drop::<AMS>,
203            },
204        }
205    }
206
207    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
208        let t = TypeId::of::<T>();
209        if self.type_id == t {
210            Some(unsafe { self.downcast_ref_unchecked() })
211        } else {
212            None
213        }
214    }
215
216    /// # Safety
217    /// If you are sure the inner type is T, you can downcast it.
218    pub unsafe fn downcast_ref_unchecked<T: Any>(&self) -> &T {
219        &*(self.svc as *const T)
220    }
221}
222
223impl<S, E> Drop for BoxedAsyncMakeService<S, E> {
224    #[inline]
225    fn drop(&mut self) {
226        unsafe { (self.vtable.drop)(self.svc) };
227    }
228}
229
230impl<S, E> AsyncMakeService for BoxedAsyncMakeService<S, E> {
231    type Service = S;
232    type Error = E;
233
234    #[inline]
235    async fn make_via_ref(
236        &self,
237        old: Option<&Self::Service>,
238    ) -> Result<Self::Service, Self::Error> {
239        unsafe { (self.vtable.make_via_ref)(self.svc, old.map(|s| s as _)) }.await
240    }
241}
242
243type LocalBoxedFuture<T, E> = Pin<Box<dyn Future<Output = Result<T, E>>>>;
244
245struct AsyncMakeServiceVtable<S, E> {
246    make_via_ref: unsafe fn(raw: *const (), old: Option<*const S>) -> LocalBoxedFuture<S, E>,
247    drop: unsafe fn(raw: *const ()),
248}
249
250unsafe fn make_via_ref<AMS, S, E>(
251    svc: *const (),
252    old: Option<*const AMS::Service>,
253) -> LocalBoxedFuture<S, E>
254where
255    AMS: AsyncMakeService<Service = S, Error = E> + 'static,
256    S: 'static,
257{
258    let svc = &*svc.cast::<AMS>();
259    let fut = AMS::make_via_ref(svc, old.map(|s| unsafe { &*s }));
260    Box::pin(fut)
261}