use std::future::Future;
use crate::IncomingMessage;
use super::batch::{BatchHandler, BatchResult, decode_batch, settle_batch};
use super::context::Context;
use super::dispatch::Workers;
use super::failure::{FailurePolicies, FailurePolicy};
use super::input::{DecodeWith, InputKind};
use super::metadata::HandlerMetadata;
pub trait BatchInjectDef: Send + Sync {
type Input: InputKind;
type Source;
type Injections;
fn source(&self) -> Self::Source;
fn workers(&self) -> Workers {
Workers::sequential()
}
fn failure_policies(&self) -> FailurePolicies {
FailurePolicies::default()
}
fn description(&self) -> Option<&str> {
None
}
fn input_schema(&self) -> Option<String> {
None
}
fn message_name(&self) -> Option<&'static str> {
None
}
fn message_description(&self) -> Option<&'static str> {
None
}
}
pub trait BatchInjectCall<S>: BatchInjectDef {
fn call(
&self,
batch: &[<Self::Input as InputKind>::Owned],
injections: &Self::Injections,
ctx: &mut Context<'_, (), S>,
) -> impl Future<Output = BatchResult> + Send;
}
pub(crate) fn batch_inject_metadata<D: BatchInjectDef>(name: String, def: &D) -> HandlerMetadata {
let mut meta = HandlerMetadata::raw(name).with_def_details(
def.description(),
def.input_schema(),
def.message_name(),
def.message_description(),
);
meta.input_type = <D::Input as InputKind>::input_label();
meta
}
pub struct BatchInjectHandler<Def: BatchInjectDef, DecodeCodec> {
pub(crate) def: Def,
pub(crate) codec: DecodeCodec,
pub(crate) injections: Def::Injections,
pub(crate) decode: FailurePolicy,
}
impl<Def: BatchInjectDef, DecodeCodec> std::fmt::Debug for BatchInjectHandler<Def, DecodeCodec> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BatchInjectHandler").finish_non_exhaustive()
}
}
impl<Msg, Def, DecodeCodec, State> BatchHandler<Msg, State> for BatchInjectHandler<Def, DecodeCodec>
where
Msg: IncomingMessage,
Def: BatchInjectCall<State>,
Def::Input: DecodeWith<DecodeCodec>,
Def::Injections: Send + Sync,
DecodeCodec: Send + Sync,
State: Send + Sync,
{
async fn handle_batch(&self, batch: Vec<Msg>, ctx: &mut Context<'_, (), State>) {
let subscription = ctx.name().to_owned();
let (values, accepted) = decode_batch::<Msg, Def::Input, DecodeCodec, State>(
batch,
&self.codec,
self.decode,
ctx,
)
.await;
if accepted.is_empty() {
return;
}
let tasks = ctx.tasks().clone();
let result = self.def.call(&values, &self.injections, ctx).await;
settle_batch(accepted, result, &subscription, &tasks).await;
}
}