1use crate::{
4 Worker, WorkerRunError,
5 activities::{ActivityContext, ActivityError, ActivityInfo},
6};
7use anyhow::bail;
8use futures_util::future::{BoxFuture, LocalBoxFuture};
9use std::{
10 any::Any,
11 collections::HashMap,
12 sync::{Arc, OnceLock},
13};
14use temporalio_common::{
15 data_converters::{
16 GenericPayloadConverter, PayloadConversionError, SerializationContext, TemporalSerializable,
17 },
18 protos::{
19 coresdk::{
20 workflow_activation::{WorkflowActivation, remove_from_cache::EvictionReason},
21 workflow_completion::WorkflowActivationCompletion,
22 },
23 temporal::api::common::v1::Payload,
24 },
25};
26
27mod activity_execution_value {
28 use super::*;
29
30 pub trait Sealed {
31 fn to_activity_payload(
32 &self,
33 context: &SerializationContext<'_>,
34 ) -> Result<Payload, PayloadConversionError>;
35 }
36
37 impl<T> Sealed for T
38 where
39 T: Any + TemporalSerializable + Send + Sync,
40 {
41 fn to_activity_payload(
42 &self,
43 context: &SerializationContext<'_>,
44 ) -> Result<Payload, PayloadConversionError> {
45 context.converter.to_payload(context, self)
46 }
47 }
48}
49
50#[async_trait::async_trait(?Send)]
55pub trait WorkerInterceptor: Send + Sync {
56 fn run_worker<'a>(
58 &'a self,
59 input: RunWorkerInput<'a>,
60 next: Next<'a, RunWorkerInput<'a>, LocalBoxFuture<'a, Result<(), WorkerRunError>>>,
61 ) -> LocalBoxFuture<'a, Result<(), WorkerRunError>> {
62 next.run(input)
63 }
64
65 fn with_workflow_replay_worker<'a>(
67 &'a self,
68 input: WithWorkflowReplayWorkerInput<'a>,
69 next: Next<
70 'a,
71 WithWorkflowReplayWorkerInput<'a>,
72 LocalBoxFuture<'a, Result<(), WorkerRunError>>,
73 >,
74 ) -> LocalBoxFuture<'a, Result<(), WorkerRunError>> {
75 next.run(input)
76 }
77
78 async fn on_workflow_activation_completion(&self, _completion: &WorkflowActivationCompletion) {}
81 fn on_shutdown(&self, _sdk_worker: &Worker) {}
84 async fn on_workflow_activation(
86 &self,
87 _activation: &WorkflowActivation,
88 ) -> Result<(), anyhow::Error> {
89 Ok(())
90 }
91}
92
93pub struct Next<'a, I, O> {
97 inner: Box<dyn FnOnce(I) -> O + Send + 'a>,
98}
99
100#[derive(Debug)]
102#[non_exhaustive]
103pub struct RunWorkerInput<'a> {
104 pub worker: &'a mut Worker,
106}
107
108impl<'a> RunWorkerInput<'a> {
109 pub(crate) fn new(worker: &'a mut Worker) -> Self {
110 Self { worker }
111 }
112}
113
114#[derive(Debug)]
116#[non_exhaustive]
117pub struct WithWorkflowReplayWorkerInput<'a> {
118 pub worker: &'a mut Worker,
120}
121
122impl<'a> WithWorkflowReplayWorkerInput<'a> {
123 pub(crate) fn new(worker: &'a mut Worker) -> Self {
124 Self { worker }
125 }
126}
127
128pub(crate) fn call_run_worker<'a>(
129 interceptors: &'a [Arc<dyn WorkerInterceptor>],
130 input: RunWorkerInput<'a>,
131 terminal: Next<'a, RunWorkerInput<'a>, LocalBoxFuture<'a, Result<(), WorkerRunError>>>,
132) -> LocalBoxFuture<'a, Result<(), WorkerRunError>> {
133 if let Some((interceptor, remaining)) = interceptors.split_first() {
134 let next = Next::new(move |input| call_run_worker(remaining, input, terminal));
135 interceptor.run_worker(input, next)
136 } else {
137 terminal.run(input)
138 }
139}
140
141pub(crate) fn call_with_workflow_replay_worker<'a>(
142 interceptors: &'a [Arc<dyn WorkerInterceptor>],
143 input: WithWorkflowReplayWorkerInput<'a>,
144 terminal: Next<
145 'a,
146 WithWorkflowReplayWorkerInput<'a>,
147 LocalBoxFuture<'a, Result<(), WorkerRunError>>,
148 >,
149) -> LocalBoxFuture<'a, Result<(), WorkerRunError>> {
150 if let Some((interceptor, remaining)) = interceptors.split_first() {
151 let next =
152 Next::new(move |input| call_with_workflow_replay_worker(remaining, input, terminal));
153 interceptor.with_workflow_replay_worker(input, next)
154 } else {
155 terminal.run(input)
156 }
157}
158
159impl<'a, I, O> Next<'a, I, O> {
160 pub(crate) fn new(f: impl FnOnce(I) -> O + Send + 'a) -> Self {
161 Self { inner: Box::new(f) }
162 }
163
164 pub fn run(self, input: I) -> O {
166 (self.inner)(input)
167 }
168}
169
170#[non_exhaustive]
172pub struct ExecuteActivityInput {
173 context: ActivityContext,
174 args: Box<dyn Any + Send + Sync>,
175}
176
177impl ExecuteActivityInput {
178 pub(crate) fn new(context: ActivityContext, args: Box<dyn Any + Send + Sync>) -> Self {
179 Self { context, args }
180 }
181
182 pub(crate) fn into_parts(self) -> (ActivityContext, Box<dyn Any + Send + Sync>) {
183 (self.context, self.args)
184 }
185
186 pub fn activity_info(&self) -> &ActivityInfo {
188 self.context.info()
189 }
190
191 pub fn headers(&self) -> &HashMap<String, Payload> {
193 self.context.headers()
194 }
195
196 pub fn headers_mut(&mut self) -> &mut HashMap<String, Payload> {
198 self.context.headers_mut()
199 }
200
201 pub fn args_ref<T: Any>(&self) -> Option<&T> {
203 self.args.downcast_ref()
204 }
205
206 pub fn args_mut<T: Any>(&mut self) -> Option<&mut T> {
208 self.args.downcast_mut()
209 }
210}
211
212pub trait ActivityExecutionValue:
214 Any + TemporalSerializable + Send + Sync + activity_execution_value::Sealed
215{
216 fn as_any(&self) -> &dyn Any;
218}
219
220impl<T> ActivityExecutionValue for T
221where
222 T: Any + TemporalSerializable + Send + Sync,
223{
224 fn as_any(&self) -> &dyn Any {
225 self
226 }
227}
228
229impl dyn ActivityExecutionValue {
230 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
232 self.as_any().downcast_ref()
233 }
234
235 pub(crate) fn serialize_payload(
236 &self,
237 context: &SerializationContext<'_>,
238 ) -> Result<Payload, PayloadConversionError> {
239 self.to_activity_payload(context)
240 }
241}
242
243pub type ExecuteActivityResult = Result<Box<dyn ActivityExecutionValue>, ActivityError>;
245
246pub type ExecuteActivityOutput<'a> = BoxFuture<'a, ExecuteActivityResult>;
248
249pub trait ActivityInboundInterceptor: Send + Sync + 'static {
253 fn execute_activity<'a>(
255 &'a self,
256 input: ExecuteActivityInput,
257 next: Next<'a, ExecuteActivityInput, ExecuteActivityOutput<'a>>,
258 ) -> ExecuteActivityOutput<'a> {
259 next.run(input)
260 }
261}
262
263pub struct FailOnNondeterminismInterceptor {}
266#[async_trait::async_trait(?Send)]
267impl WorkerInterceptor for FailOnNondeterminismInterceptor {
268 async fn on_workflow_activation(
269 &self,
270 activation: &WorkflowActivation,
271 ) -> Result<(), anyhow::Error> {
272 if matches!(
273 activation.eviction_reason(),
274 Some(EvictionReason::Nondeterminism)
275 ) {
276 bail!("Workflow is being evicted because of nondeterminism! {activation}");
277 }
278 Ok(())
279 }
280}
281
282#[derive(Default)]
284pub struct ReturnWorkflowExitValueInterceptor {
285 result_value: Arc<OnceLock<Payload>>,
286}
287
288impl ReturnWorkflowExitValueInterceptor {
289 pub fn result_handle(&self) -> Arc<OnceLock<Payload>> {
291 self.result_value.clone()
292 }
293}
294
295#[async_trait::async_trait(?Send)]
296impl WorkerInterceptor for ReturnWorkflowExitValueInterceptor {
297 async fn on_workflow_activation_completion(&self, c: &WorkflowActivationCompletion) {
298 if let Some(v) = c.complete_workflow_execution_value() {
299 let _ = self.result_value.set(v.clone());
300 }
301 }
302}