apalis_workflow/sequential/and_then/
mod.rs1use std::{marker::PhantomData, task::Context};
2
3use apalis_core::{
4 backend::{Backend, BackendConfig, WireFormatBackend, codec::Codec},
5 error::BoxDynError,
6 task::task_fn::{TaskFn, task_fn},
7 task::{Task, task_id::GenerateId},
8};
9use futures_util::{
10 FutureExt, Sink,
11 future::{BoxFuture, ready},
12};
13use serde::Serialize;
14use tower::{Service, ServiceBuilder, layer::layer_fn};
15
16use crate::{
17 SteppedService,
18 sequential::context::StepContext,
19 sequential::router::{GoTo, StepResponse, WorkflowRouter},
20 sequential::service::handle_step_result,
21 sequential::step::{Layer, Stack, Step},
22 sequential::workflow::SteppedFlow,
23};
24
25#[derive(Clone, Debug)]
27pub struct AndThen<F> {
28 then_fn: F,
29}
30
31impl<F> AndThen<F> {
32 pub fn new(then_fn: F) -> Self {
34 Self { then_fn }
35 }
36}
37
38#[derive(Clone, Debug)]
40pub struct AndThenStep<F, S> {
41 then_fn: F,
42 step: S,
43}
44
45impl<S, F> Layer<S> for AndThen<F>
46where
47 F: Clone,
48{
49 type Step = AndThenStep<F, S>;
50
51 fn layer(&self, step: S) -> Self::Step {
52 AndThenStep {
53 then_fn: self.then_fn.clone(),
54 step,
55 }
56 }
57}
58
59impl<F, Input, S, B, CodecError, Err> Step<Input, B> for AndThenStep<F, S>
60where
61 B: Backend<Error = Err>
62 + WireFormatBackend
63 + BackendConfig
64 + Sink<Task<B::Compact>, Error = Err>
65 + Send
66 + Sync
67 + Unpin
68 + Clone
69 + 'static,
70 F: Service<Task<Input>, Error = BoxDynError> + Send + Sync + 'static + Clone,
71 S: Step<F::Response, B>,
72 Input: Send + Sync + 'static,
73 F::Future: Send + 'static,
74 F::Error: Into<BoxDynError> + Send + 'static,
75 B::Codec: Codec<F::Response, Error = CodecError, Compact = B::Compact>
76 + Codec<Input, Error = CodecError, Compact = B::Compact>
77 + Codec<S::Response, Error = CodecError, Compact = B::Compact>
78 + Send
79 + Sync
80 + Clone
81 + 'static,
82 CodecError: std::error::Error + Send + Sync + 'static,
83 B::Id: GenerateId + Send + Sync + 'static,
84 S::Response: Send + 'static,
85 B::Compact: Send + 'static,
86 F::Response: Send + Serialize + 'static,
87 Err: std::error::Error + Send + Sync + 'static,
88{
89 type Response = F::Response;
90 type Error = F::Error;
91 fn register(&mut self, ctx: &mut WorkflowRouter<B>) -> Result<(), BoxDynError> {
92 let svc = ServiceBuilder::new()
93 .layer(layer_fn(|s| AndThenService {
94 service: s,
95 _marker: PhantomData::<fn(B, Input) -> ()>,
96 }))
97 .map_response(|res: F::Response| GoTo::Next(res))
98 .service(self.then_fn.clone());
99 let svc = SteppedService::<B::Compact>::new(svc);
100 let count = ctx.steps.len();
101 ctx.steps.insert(count, svc);
102 self.step.register(ctx)
103 }
104}
105
106#[derive(Debug)]
108pub struct AndThenService<Svc, Backend, Cur> {
109 service: Svc,
110 _marker: PhantomData<fn(Backend, Cur) -> ()>,
111}
112
113impl<Svc: Clone, Backend, Cur> Clone for AndThenService<Svc, Backend, Cur> {
114 fn clone(&self) -> Self {
115 Self {
116 service: self.service.clone(),
117 _marker: PhantomData,
118 }
119 }
120}
121
122impl<Svc, Backend, Cur> AndThenService<Svc, Backend, Cur> {
123 pub fn new(service: Svc) -> Self {
125 Self {
126 service,
127 _marker: PhantomData,
128 }
129 }
130}
131
132impl<S, B, Cur, Res, CodecErr, Err> Service<Task<B::Compact>> for AndThenService<S, B, Cur>
133where
134 S: Service<Task<Cur>, Response = GoTo<Res>>,
135 S::Future: Send + 'static,
136 B: Backend<Error = Err>
137 + WireFormatBackend
138 + Sink<Task<B::Compact>, Error = Err>
139 + BackendConfig
140 + Clone
141 + Send
142 + Unpin
143 + Sync
144 + 'static,
145 B::Codec: Codec<Cur, Compact = B::Compact, Error = CodecErr>
146 + Codec<Res, Compact = B::Compact, Error = CodecErr>
147 + Send
148 + Clone
149 + Sync,
150 S::Error: Into<BoxDynError> + Send + 'static,
151 CodecErr: Into<BoxDynError> + Send + 'static,
152 Cur: Send + 'static,
153 B::Id: GenerateId + Send + Sync + 'static,
154 Res: Send + Serialize + 'static,
155 B::Compact: Send + 'static,
156 Err: std::error::Error + Send + Sync + 'static,
157{
158 type Response = GoTo<StepResponse>;
159 type Error = BoxDynError;
160 type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
161
162 fn poll_ready(&mut self, cx: &mut Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
163 self.service.poll_ready(cx).map_err(|e| e.into())
164 }
165
166 fn call(&mut self, request: Task<B::Compact>) -> Self::Future {
167 let mut ctx = request.data().get::<StepContext<B>>().cloned().unwrap();
168 let codec = ctx.backend.codec();
169 let compacted = request.try_map_args(|t| B::Codec::decode(codec, &t));
170 match compacted {
171 Ok(task) => {
172 let fut = self.service.call(task);
173 async move {
174 let res = fut.await.map_err(|e| e.into())?;
175 Ok(handle_step_result(&mut ctx, res).await?)
176 }
177 .boxed()
178 }
179 Err(e) => ready(Err(e.into())).boxed(),
180 }
181 }
182}
183
184impl<Start, Cur, B, L> SteppedFlow<Start, Cur, B, L>
185where
186 B: Backend,
187{
188 #[allow(clippy::type_complexity)]
202 pub fn and_then<F, O, FnArgs>(
203 self,
204 and_then: F,
205 ) -> SteppedFlow<Start, O, B, Stack<AndThen<TaskFn<F, Cur, FnArgs>>, L>>
206 where
207 TaskFn<F, Cur, FnArgs>: Service<Task<Cur>, Response = O>,
208 {
209 self.add_step(AndThen {
210 then_fn: task_fn(and_then),
211 })
212 }
213}