use super::{CallOutcome, ServerInterceptorChain};
use crate::call_context::CallContext;
use crate::error::ServerResult;
impl ServerInterceptorChain {
pub(crate) const fn begin<'a>(&'a self, ctx: &'a CallContext) -> InterceptedCall<'a> {
InterceptedCall {
chain: self,
ctx,
entered: 0,
}
}
}
pub struct InterceptedCall<'a> {
chain: &'a ServerInterceptorChain,
ctx: &'a CallContext,
entered: usize,
}
impl InterceptedCall<'_> {
pub async fn before(&mut self) -> a2a_protocol_types::error::A2aResult<()> {
let chain = self.chain;
for interceptor in &chain.interceptors {
self.entered += 1;
interceptor.before(self.ctx).await?;
}
Ok(())
}
pub async fn finish<T>(mut self, result: ServerResult<T>) -> ServerResult<T> {
let result = match result {
Ok(out) => match self.chain.run_after(self.ctx).await {
Ok(()) => Ok(out),
Err(e) => Err(e.into()),
},
Err(e) => Err(e),
};
let outcome = match &result {
Ok(_) => CallOutcome::Succeeded,
Err(e) => CallOutcome::Failed(e),
};
let chain = self.chain;
for interceptor in chain.interceptors.iter().take(self.entered).rev() {
self.entered -= 1;
interceptor.on_complete(self.ctx, outcome).await;
}
result
}
}
impl Drop for InterceptedCall<'_> {
fn drop(&mut self) {
if self.entered == 0 {
return;
}
let Ok(runtime) = tokio::runtime::Handle::try_current() else {
return;
};
let owed: Vec<_> = self
.chain
.interceptors
.iter()
.take(self.entered)
.rev()
.cloned()
.collect();
let ctx = self.ctx.clone();
let tenant = crate::store::tenant::TenantContext::current();
drop(runtime.spawn(crate::rpc_span::in_current_span(
crate::store::tenant::TenantContext::scope(tenant, async move {
for interceptor in owed {
interceptor.on_complete(&ctx, CallOutcome::Cancelled).await;
}
}),
)));
}
}