use crate::{BindCtx, Ctx, Service, readiness::ReadyScope};
#[derive(Debug)]
pub struct ServiceBinding<S> {
service: S,
scope: ReadyScope,
}
impl<S> ServiceBinding<S> {
pub fn new(service: S) -> Self {
Self {
service,
scope: ReadyScope::new(),
}
}
fn enter<'a, T, St>(&'a self, ctx: Ctx<'a, Self, St>) -> Ctx<'a, T, St> {
ctx.scope().assert_dependency(&self.scope);
ctx.with_scope(&self.scope)
}
#[cfg(test)]
pub(crate) fn is_ready(&self) -> bool {
self.scope.is_ready()
}
}
impl<S: Clone> Clone for ServiceBinding<S> {
fn clone(&self) -> Self {
Self::new(self.service.clone())
}
}
impl<S, St, Req> Service<St, Req> for ServiceBinding<S>
where
S: Service<St, Req>,
{
type Res = S::Res;
type Error = S::Error;
fn bind(&self, ctx: BindCtx<'_>) {
ctx.depend_on(&self.scope);
self.service.bind(BindCtx::new(&self.scope));
}
async fn ready(&self, ctx: Ctx<'_, Self, St>) -> Result<(), Self::Error> {
let ctx = self.enter(ctx);
self.scope.check(|| self.service.ready(ctx)).await
}
async fn call(&self, req: Req, ctx: Ctx<'_, Self, St>) -> Result<Self::Res, Self::Error> {
let ctx = self.enter::<Self, _>(ctx);
if !ctx.is_call_nowait() {
ctx.ready_cached(self).await?;
}
self.service.call(req, ctx.cast()).await
}
async fn shutdown(&self, ctx: Ctx<'_, Self, St>) {
self.service.shutdown(self.enter(ctx)).await;
}
}
#[cfg(test)]
#[path = "../tests/unit/service_binding.rs"]
mod tests;