1use crate::{
85 ActivityOptions, BaseWorkflowContext, CancellableFuture, CancellableFutureWithReason,
86 ChildWorkflowOptions, ContinueAsNewOptions, ExternalWorkflowHandle, LocalActivityOptions,
87 SignalWorkflowOptions, StartChildWorkflowOutput, StartedChildWorkflow, TimerOptions,
88 WorkflowCancellationToken, WorkflowContextView, WorkflowRandomStream,
89 cancellation::WorkflowCancellationRegistration,
90 runtime::{
91 entry::WorkflowError,
92 model::{CancelExternalWfResult, TimerResult, WorkflowResult, WorkflowTermination},
93 },
94};
95use futures_util::{
96 FutureExt,
97 future::{Fuse, FusedFuture, LocalBoxFuture},
98};
99use std::{
100 any::Any,
101 collections::HashMap,
102 convert::Infallible,
103 future::Future,
104 pin::Pin,
105 rc::Rc,
106 sync::Arc,
107 task::{Context, Poll},
108 time::SystemTime,
109};
110use temporalio_common_wasm::{
111 ActivityDefinition, WorkflowDefinition,
112 data_converters::{
113 GenericPayloadConverter, PayloadConversionError, PayloadConverter, SerializationContext,
114 SerializationContextData, TemporalDeserializable, TemporalSerializable,
115 WorkflowSerializationContext,
116 },
117 error::{
118 ActivityExecutionError, ChildWorkflowExecutionError, ChildWorkflowStartError,
119 WorkflowSignalError,
120 },
121 protos::temporal::api::common::v1::Payload,
122 search_attributes::SearchAttributes,
123};
124
125#[cfg(feature = "experimental")]
126pub(crate) use nexus::call_start_nexus_operation;
127#[cfg(feature = "experimental")]
128pub use nexus::{StartNexusOperationInput, StartNexusOperationResult};
129
130mod workflow_output_value {
131 use super::*;
132
133 pub trait Sealed {
134 fn to_workflow_payload(
135 &self,
136 context: &SerializationContext<'_>,
137 ) -> Result<Payload, PayloadConversionError>;
138
139 fn to_workflow_payloads(
140 &self,
141 context: &SerializationContext<'_>,
142 ) -> Result<Vec<Payload>, PayloadConversionError>;
143 }
144
145 impl<T> Sealed for T
146 where
147 T: Any + TemporalSerializable,
148 {
149 fn to_workflow_payload(
150 &self,
151 context: &SerializationContext<'_>,
152 ) -> Result<Payload, PayloadConversionError> {
153 context.converter.to_payload(context, self)
154 }
155
156 fn to_workflow_payloads(
157 &self,
158 context: &SerializationContext<'_>,
159 ) -> Result<Vec<Payload>, PayloadConversionError> {
160 context.converter.to_payloads(context, self)
161 }
162 }
163}
164
165pub trait WorkflowOutputValue: Any + TemporalSerializable + workflow_output_value::Sealed {
167 fn as_any(&self) -> &dyn Any;
169}
170
171impl<T> WorkflowOutputValue for T
172where
173 T: Any + TemporalSerializable,
174{
175 fn as_any(&self) -> &dyn Any {
176 self
177 }
178}
179
180impl dyn WorkflowOutputValue {
181 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
183 self.as_any().downcast_ref()
184 }
185
186 pub(crate) fn serialize_payload(
187 &self,
188 context: &SerializationContext<'_>,
189 ) -> Result<Payload, PayloadConversionError> {
190 self.to_workflow_payload(context)
191 }
192
193 pub(crate) fn serialize_payloads(
194 &self,
195 context: &SerializationContext<'_>,
196 ) -> Result<Vec<Payload>, PayloadConversionError> {
197 self.to_workflow_payloads(context)
198 }
199}
200
201pub(crate) fn serialize_workflow_output(
202 output: &dyn WorkflowOutputValue,
203 converter: &PayloadConverter,
204) -> Result<Payload, PayloadConversionError> {
205 let context_data = SerializationContextData::Workflow(WorkflowSerializationContext::new());
206 let ctx = SerializationContext::new(&context_data, converter);
207 output.serialize_payload(&ctx)
208}
209
210pub type ExecuteWorkflowResult = WorkflowResult<Box<dyn WorkflowOutputValue>>;
212
213pub type HandleSignalResult = Result<(), WorkflowError>;
215
216pub type HandleUpdateResult = Result<Box<dyn WorkflowOutputValue>, WorkflowError>;
218
219pub type HandleQueryResult = Result<Box<dyn WorkflowOutputValue>, WorkflowError>;
221
222pub type ValidateUpdateResult = Result<(), WorkflowError>;
224
225pub struct WorkflowInterceptorFuture<'a, T>(LocalBoxFuture<'a, T>);
237
238impl<'a, T> WorkflowInterceptorFuture<'a, T> {
239 pub fn new(fut: impl Future<Output = T> + 'a) -> Self {
241 Self(fut.boxed_local())
242 }
243}
244
245impl<'a, T> Unpin for WorkflowInterceptorFuture<'a, T> {}
246
247impl<T> Future for WorkflowInterceptorFuture<'_, T> {
248 type Output = T;
249
250 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
251 self.0.as_mut().poll(cx)
252 }
253}
254
255pub struct WorkflowNext<'a, I, O> {
257 inner: Box<dyn FnOnce(I) -> O + 'a>,
258}
259
260impl<'a, I, O> WorkflowNext<'a, I, O> {
261 pub(crate) fn new(f: impl FnOnce(I) -> O + 'a) -> Self {
262 Self { inner: Box::new(f) }
263 }
264
265 pub fn run(self, input: I) -> O {
267 (self.inner)(input)
268 }
269}
270
271#[derive(Clone)]
273pub struct WorkflowInterceptorContext {
274 base: BaseWorkflowContext,
275}
276
277impl WorkflowInterceptorContext {
278 pub(crate) fn new(base: BaseWorkflowContext) -> Self {
279 Self { base }
280 }
281
282 pub fn workflow_id(&self) -> &str {
284 self.base.workflow_id()
285 }
286
287 pub fn run_id(&self) -> &str {
289 self.base.run_id()
290 }
291
292 pub fn namespace(&self) -> &str {
294 self.base.namespace()
295 }
296
297 pub fn task_queue(&self) -> &str {
299 self.base.task_queue()
300 }
301
302 pub fn workflow_type(&self) -> &str {
304 self.base.workflow_type()
305 }
306
307 pub fn workflow_time(&self) -> Option<SystemTime> {
309 self.base.workflow_time()
310 }
311
312 pub fn history_length(&self) -> u32 {
314 self.base.history_length()
315 }
316
317 pub fn search_attributes(&self) -> SearchAttributes {
319 self.base.search_attributes()
320 }
321
322 pub fn is_replaying(&self) -> bool {
324 self.base.is_replaying()
325 }
326
327 pub fn is_replaying_history_events(&self) -> bool {
329 self.base.is_replaying_history_events()
330 }
331
332 pub fn payload_converter(&self) -> &PayloadConverter {
334 self.base.payload_converter()
335 }
336
337 pub fn cancellation_token(&self) -> WorkflowCancellationToken {
339 self.base.cancellation_token()
340 }
341
342 pub fn random_stream(&self, name: impl Into<String>) -> WorkflowRandomStream {
349 self.base.random_stream(name)
350 }
351
352 pub fn timer<T: Into<TimerOptions>>(
354 &self,
355 opts: T,
356 ) -> impl CancellableFuture<Output = TimerResult> + use<T> {
357 self.base.timer(opts)
358 }
359
360 pub fn execute_activity<AD: ActivityDefinition>(
362 &self,
363 activity: AD,
364 input: impl Into<AD::Input>,
365 opts: ActivityOptions,
366 ) -> impl CancellableFuture<Output = Result<AD::Output, ActivityExecutionError>>
367 where
368 AD::Output: TemporalDeserializable,
369 {
370 self.base.execute_activity(activity, input, opts)
371 }
372
373 pub fn execute_local_activity<AD: ActivityDefinition>(
375 &self,
376 activity: AD,
377 input: impl Into<AD::Input>,
378 opts: LocalActivityOptions,
379 ) -> impl CancellableFuture<Output = Result<AD::Output, ActivityExecutionError>>
380 where
381 AD::Output: TemporalDeserializable,
382 {
383 self.base.execute_local_activity(activity, input, opts)
384 }
385
386 pub fn start_child_workflow<WD: WorkflowDefinition + 'static>(
388 &self,
389 workflow: WD,
390 input: impl Into<WD::Input>,
391 opts: ChildWorkflowOptions,
392 ) -> impl CancellableFutureWithReason<
393 Output = Result<StartedChildWorkflow<WD>, ChildWorkflowStartError>,
394 >
395 where
396 WD::Output: TemporalDeserializable,
397 {
398 self.base.start_child_workflow(workflow, input, opts)
399 }
400
401 pub fn external_workflow(
403 &self,
404 workflow_id: impl Into<String>,
405 run_id: Option<String>,
406 ) -> ExternalWorkflowHandle {
407 self.base.external_workflow(workflow_id, run_id)
408 }
409}
410
411#[derive(Clone)]
413pub struct SyncWorkflowInterceptorContext {
414 base: BaseWorkflowContext,
415}
416
417impl SyncWorkflowInterceptorContext {
418 pub(crate) fn new(base: BaseWorkflowContext) -> Self {
419 Self { base }
420 }
421
422 pub fn workflow_id(&self) -> &str {
424 self.base.workflow_id()
425 }
426
427 pub fn run_id(&self) -> &str {
429 self.base.run_id()
430 }
431
432 pub fn namespace(&self) -> &str {
434 self.base.namespace()
435 }
436
437 pub fn task_queue(&self) -> &str {
439 self.base.task_queue()
440 }
441
442 pub fn workflow_type(&self) -> &str {
444 self.base.workflow_type()
445 }
446
447 pub fn cancellation_token(&self) -> WorkflowCancellationToken {
449 self.base.cancellation_token()
450 }
451
452 pub fn workflow_time(&self) -> Option<SystemTime> {
454 self.base.workflow_time()
455 }
456
457 pub fn history_length(&self) -> u32 {
459 self.base.history_length()
460 }
461
462 pub fn search_attributes(&self) -> SearchAttributes {
464 self.base.search_attributes()
465 }
466
467 pub fn is_replaying(&self) -> bool {
469 self.base.is_replaying()
470 }
471
472 pub fn is_replaying_history_events(&self) -> bool {
474 self.base.is_replaying_history_events()
475 }
476
477 pub fn payload_converter(&self) -> &PayloadConverter {
479 self.base.payload_converter()
480 }
481}
482
483struct DecodedInput {
484 value: Option<Box<dyn Any>>,
485 headers: HashMap<String, Payload>,
486}
487
488impl DecodedInput {
489 fn new(value: Option<Box<dyn Any>>, headers: HashMap<String, Payload>) -> Self {
490 Self { value, headers }
491 }
492
493 fn input_ref<T: Any>(&self) -> Option<&T> {
494 self.value.as_ref()?.downcast_ref()
495 }
496
497 fn input_mut<T: Any>(&mut self) -> Option<&mut T> {
498 self.value.as_mut()?.downcast_mut()
499 }
500
501 fn headers(&self) -> &HashMap<String, Payload> {
502 &self.headers
503 }
504
505 fn headers_mut(&mut self) -> &mut HashMap<String, Payload> {
506 &mut self.headers
507 }
508
509 fn into_parts(self) -> (Option<Box<dyn Any>>, HashMap<String, Payload>) {
510 (self.value, self.headers)
511 }
512}
513
514#[non_exhaustive]
519pub struct InitializeWorkflowInput {
520 decoded: DecodedInput,
521}
522
523impl InitializeWorkflowInput {
524 pub(crate) fn new(value: Option<Box<dyn Any>>, headers: HashMap<String, Payload>) -> Self {
525 Self {
526 decoded: DecodedInput::new(value, headers),
527 }
528 }
529
530 pub(crate) fn into_parts(self) -> (Option<Box<dyn Any>>, HashMap<String, Payload>) {
531 self.decoded.into_parts()
532 }
533
534 pub fn input_ref<T: Any>(&self) -> Option<&T> {
536 self.decoded.input_ref()
537 }
538
539 pub fn input_mut<T: Any>(&mut self) -> Option<&mut T> {
541 self.decoded.input_mut()
542 }
543
544 pub fn headers(&self) -> &HashMap<String, Payload> {
546 self.decoded.headers()
547 }
548
549 pub fn headers_mut(&mut self) -> &mut HashMap<String, Payload> {
551 self.decoded.headers_mut()
552 }
553}
554
555pub struct InitializeWorkflowOutput {
557 _private: (),
558}
559
560impl InitializeWorkflowOutput {
561 pub(crate) fn new() -> Self {
562 Self { _private: () }
563 }
564}
565
566#[non_exhaustive]
571pub struct ExecuteWorkflowInput {
572 decoded: DecodedInput,
573}
574
575impl ExecuteWorkflowInput {
576 pub(crate) fn new(value: Option<Box<dyn Any>>, headers: HashMap<String, Payload>) -> Self {
577 Self {
578 decoded: DecodedInput::new(value, headers),
579 }
580 }
581
582 pub(crate) fn into_parts(self) -> (Option<Box<dyn Any>>, HashMap<String, Payload>) {
583 self.decoded.into_parts()
584 }
585
586 pub fn input_ref<T: Any>(&self) -> Option<&T> {
588 self.decoded.input_ref()
589 }
590
591 pub fn input_mut<T: Any>(&mut self) -> Option<&mut T> {
593 self.decoded.input_mut()
594 }
595
596 pub fn headers(&self) -> &HashMap<String, Payload> {
598 self.decoded.headers()
599 }
600
601 pub fn headers_mut(&mut self) -> &mut HashMap<String, Payload> {
603 self.decoded.headers_mut()
604 }
605}
606
607macro_rules! handler_input {
608 ($name:ident, $doc:literal, $field:ident, $field_doc:literal $(, $id_field:ident, $id_doc:literal)?) => {
609 #[doc = $doc]
610 #[non_exhaustive]
611 pub struct $name {
612 $($id_field: String,)?
613 $field: String,
614 decoded: DecodedInput,
615 }
616
617 impl $name {
618 pub(crate) fn new(
619 $($id_field: String,)?
620 $field: String,
621 value: Box<dyn Any>,
622 headers: HashMap<String, Payload>,
623 ) -> Self {
624 Self {
625 $($id_field,)?
626 $field,
627 decoded: DecodedInput::new(Some(value), headers),
628 }
629 }
630
631 pub(crate) fn into_parts(self) -> (String, Box<dyn Any>, HashMap<String, Payload>) {
632 let (value, headers) = self.decoded.into_parts();
633 (
634 self.$field,
635 value.expect("handler input must exist after typed decode"),
636 headers,
637 )
638 }
639
640 #[doc = $field_doc]
641 pub fn name(&self) -> &str {
642 &self.$field
643 }
644
645 $(
646 #[doc = $id_doc]
647 pub fn id(&self) -> &str {
648 &self.$id_field
649 }
650 )?
651
652 pub fn input_ref<T: Any>(&self) -> Option<&T> {
654 self.decoded.input_ref()
655 }
656
657 pub fn input_mut<T: Any>(&mut self) -> Option<&mut T> {
659 self.decoded.input_mut()
660 }
661
662 pub fn headers(&self) -> &HashMap<String, Payload> {
664 self.decoded.headers()
665 }
666
667 pub fn headers_mut(&mut self) -> &mut HashMap<String, Payload> {
669 self.decoded.headers_mut()
670 }
671 }
672 };
673}
674
675handler_input!(
676 HandleSignalInput,
677 "Input passed to [`WorkflowInterceptor::handle_signal`].",
678 signal_name,
679 "Return the signal name."
680);
681
682handler_input!(
683 HandleUpdateInput,
684 "Input passed to [`WorkflowInterceptor::handle_update`].",
685 update_name,
686 "Return the update name.",
687 update_id,
688 "Return the update ID."
689);
690
691handler_input!(
692 HandleQueryInput,
693 "Input passed to [`WorkflowInterceptor::handle_query`].",
694 query_name,
695 "Return the query name.",
696 query_id,
697 "Return the query ID."
698);
699
700#[non_exhaustive]
702pub struct ValidateUpdateInput {
703 update_id: String,
704 update_name: String,
705 decoded: DecodedInput,
706}
707
708impl ValidateUpdateInput {
709 pub(crate) fn new(
710 update_id: String,
711 update_name: String,
712 value: Box<dyn Any>,
713 headers: HashMap<String, Payload>,
714 ) -> Self {
715 Self {
716 update_id,
717 update_name,
718 decoded: DecodedInput::new(Some(value), headers),
719 }
720 }
721
722 pub(crate) fn into_parts(self) -> (String, Box<dyn Any>, HashMap<String, Payload>) {
723 let (value, headers) = self.decoded.into_parts();
724 (
725 self.update_name,
726 value.expect("update validation input must exist after typed decode"),
727 headers,
728 )
729 }
730
731 pub fn name(&self) -> &str {
733 &self.update_name
734 }
735
736 pub fn id(&self) -> &str {
738 &self.update_id
739 }
740
741 pub fn input_ref<T: Any>(&self) -> Option<&T> {
743 self.decoded.input_ref()
744 }
745
746 pub fn input_mut<T: Any>(&mut self) -> Option<&mut T> {
748 self.decoded.input_mut()
749 }
750
751 pub fn headers(&self) -> &HashMap<String, Payload> {
753 self.decoded.headers()
754 }
755
756 pub fn headers_mut(&mut self) -> &mut HashMap<String, Payload> {
758 self.decoded.headers_mut()
759 }
760}
761
762pub trait WorkflowOutboundValue: Any {
764 fn as_any(&self) -> &dyn Any;
766
767 fn into_any(self: Box<Self>) -> Box<dyn Any>;
769}
770
771impl<T: Any> WorkflowOutboundValue for T {
772 fn as_any(&self) -> &dyn Any {
773 self
774 }
775
776 fn into_any(self: Box<Self>) -> Box<dyn Any> {
777 self
778 }
779}
780
781impl dyn WorkflowOutboundValue {
782 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
784 self.as_any().downcast_ref()
785 }
786
787 pub fn downcast<T: Any>(self: Box<Self>) -> Result<Box<T>, Box<dyn Any>> {
789 self.into_any().downcast()
790 }
791}
792
793pub struct WorkflowOutboundFuture<T> {
795 state: WorkflowOutboundFutureState<T>,
796}
797
798enum WorkflowOutboundFutureState<T> {
799 Running(Fuse<LocalBoxFuture<'static, T>>),
800 Prefetched(Option<T>),
801 Terminated,
802}
803
804impl<T> WorkflowOutboundFuture<T> {
805 pub fn new(future: impl Future<Output = T> + 'static) -> Self {
807 Self {
808 state: WorkflowOutboundFutureState::Running(future.boxed_local().fuse()),
809 }
810 }
811
812 pub fn ready(value: T) -> Self
814 where
815 T: 'static,
816 {
817 Self::new(async move { value })
818 }
819
820 pub fn map<U>(self, map: impl FnOnce(T) -> U + 'static) -> WorkflowOutboundFuture<U>
822 where
823 T: 'static,
824 U: 'static,
825 {
826 WorkflowOutboundFuture::new(async move { map(self.await) })
827 }
828
829 pub(crate) fn poll_for_construction(&mut self, cx: &mut Context<'_>) {
830 let WorkflowOutboundFutureState::Running(future) = &mut self.state else {
831 return;
832 };
833 if let Poll::Ready(value) = future.poll_unpin(cx) {
834 self.state = WorkflowOutboundFutureState::Prefetched(Some(value));
835 }
836 }
837}
838
839impl<T> Unpin for WorkflowOutboundFuture<T> {}
840
841impl<T> Future for WorkflowOutboundFuture<T> {
842 type Output = T;
843
844 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
845 match &mut self.state {
846 WorkflowOutboundFutureState::Running(future) => {
847 let result = future.poll_unpin(cx);
848 if result.is_ready() {
849 self.state = WorkflowOutboundFutureState::Terminated;
850 }
851 result
852 }
853 WorkflowOutboundFutureState::Prefetched(value) => {
854 let value = value
855 .take()
856 .expect("outbound future polled after completion");
857 self.state = WorkflowOutboundFutureState::Terminated;
858 Poll::Ready(value)
859 }
860 WorkflowOutboundFutureState::Terminated => {
861 panic!("outbound future polled after completion")
862 }
863 }
864 }
865}
866
867impl<T> FusedFuture for WorkflowOutboundFuture<T> {
868 fn is_terminated(&self) -> bool {
869 matches!(self.state, WorkflowOutboundFutureState::Terminated)
870 }
871}
872
873#[derive(Clone)]
875pub struct WorkflowCancellationHandle {
876 cancel: Rc<dyn Fn(Option<String>)>,
877}
878
879impl WorkflowCancellationHandle {
880 pub fn new(cancel: impl Fn(Option<String>) + 'static) -> Self {
882 Self {
883 cancel: Rc::new(cancel),
884 }
885 }
886
887 pub(crate) fn noop() -> Self {
888 Self::new(|_| {})
889 }
890
891 pub fn cancel(&self, reason: Option<String>) {
893 (self.cancel)(reason);
894 }
895}
896
897pub struct CancellableWorkflowOutboundFuture<T> {
899 inner: WorkflowOutboundFuture<T>,
900 cancellation: WorkflowCancellationHandle,
901 cancellation_registration: Option<WorkflowCancellationRegistration>,
902}
903
904impl<T> CancellableWorkflowOutboundFuture<T> {
905 pub fn new(
907 future: impl Future<Output = T> + 'static,
908 cancellation: WorkflowCancellationHandle,
909 ) -> Self {
910 Self {
911 inner: WorkflowOutboundFuture::new(future),
912 cancellation,
913 cancellation_registration: None,
914 }
915 }
916
917 pub(crate) fn with_cancellation_token(mut self, token: WorkflowCancellationToken) -> Self {
918 let cancellation = self.cancellation.clone();
919 self.cancellation_registration = Some(token.register(move |reason| {
920 cancellation.cancel(reason);
921 }));
922 self
923 }
924
925 pub(crate) fn unregister_cancellation(&mut self) {
926 if let Some(registration) = &mut self.cancellation_registration {
927 registration.unregister();
928 }
929 }
930
931 pub fn cancellation_handle(&self) -> WorkflowCancellationHandle {
933 self.cancellation.clone()
934 }
935
936 pub fn map<U>(self, map: impl FnOnce(T) -> U + 'static) -> CancellableWorkflowOutboundFuture<U>
938 where
939 T: 'static,
940 U: 'static,
941 {
942 let cancellation = self.cancellation.clone();
943 CancellableWorkflowOutboundFuture::new(async move { map(self.await) }, cancellation)
944 }
945
946 pub(crate) fn poll_for_construction(&mut self, cx: &mut Context<'_>) {
947 self.inner.poll_for_construction(cx);
948 }
949}
950
951impl<T> Unpin for CancellableWorkflowOutboundFuture<T> {}
952
953impl<T> Future for CancellableWorkflowOutboundFuture<T> {
954 type Output = T;
955
956 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
957 let result = Pin::new(&mut self.inner).poll(cx);
958 if result.is_ready() {
959 self.unregister_cancellation();
960 }
961 result
962 }
963}
964
965impl<T> FusedFuture for CancellableWorkflowOutboundFuture<T> {
966 fn is_terminated(&self) -> bool {
967 self.inner.is_terminated()
968 }
969}
970
971impl<T> CancellableFuture for CancellableWorkflowOutboundFuture<T> {
972 fn cancel(&self) {
973 if !self.inner.is_terminated() {
974 self.cancellation.cancel(None);
975 }
976 }
977}
978
979impl<T> CancellableFutureWithReason for CancellableWorkflowOutboundFuture<T> {
980 fn cancel_with_reason(&self, reason: String) {
981 if !self.inner.is_terminated() {
982 self.cancellation.cancel(Some(reason));
983 }
984 }
985}
986
987macro_rules! typed_outbound_input {
988 ($name:ident) => {
989 impl $name {
990 pub fn input_ref<T: Any>(&self) -> Option<&T> {
992 self.decoded.input_ref()
993 }
994
995 pub fn input_mut<T: Any>(&mut self) -> Option<&mut T> {
997 self.decoded.input_mut()
998 }
999
1000 pub fn headers(&self) -> &HashMap<String, Payload> {
1002 self.decoded.headers()
1003 }
1004
1005 pub fn headers_mut(&mut self) -> &mut HashMap<String, Payload> {
1007 self.decoded.headers_mut()
1008 }
1009 }
1010 };
1011}
1012
1013#[non_exhaustive]
1015pub struct StartTimerInput {
1016 options: TimerOptions,
1017}
1018
1019impl StartTimerInput {
1020 pub(crate) fn new(options: TimerOptions) -> Self {
1021 Self { options }
1022 }
1023
1024 pub(crate) fn into_options(self) -> TimerOptions {
1025 self.options
1026 }
1027
1028 pub fn options(&self) -> &TimerOptions {
1030 &self.options
1031 }
1032
1033 pub fn options_mut(&mut self) -> &mut TimerOptions {
1035 &mut self.options
1036 }
1037}
1038
1039#[non_exhaustive]
1041pub struct ScheduleActivityInput {
1042 activity_type: String,
1043 decoded: DecodedInput,
1044 options: ActivityOptions,
1045}
1046
1047impl ScheduleActivityInput {
1048 pub(crate) fn new(
1049 activity_type: String,
1050 input: Box<dyn Any>,
1051 options: ActivityOptions,
1052 ) -> Self {
1053 Self {
1054 activity_type,
1055 decoded: DecodedInput::new(Some(input), HashMap::new()),
1056 options,
1057 }
1058 }
1059
1060 pub(crate) fn into_parts(
1061 self,
1062 ) -> (
1063 String,
1064 Box<dyn Any>,
1065 HashMap<String, Payload>,
1066 ActivityOptions,
1067 ) {
1068 let (input, headers) = self.decoded.into_parts();
1069 (
1070 self.activity_type,
1071 input.expect("activity input must exist"),
1072 headers,
1073 self.options,
1074 )
1075 }
1076
1077 pub fn activity_type(&self) -> &str {
1079 &self.activity_type
1080 }
1081
1082 pub fn activity_type_mut(&mut self) -> &mut String {
1084 &mut self.activity_type
1085 }
1086
1087 pub fn options(&self) -> &ActivityOptions {
1089 &self.options
1090 }
1091
1092 pub fn options_mut(&mut self) -> &mut ActivityOptions {
1094 &mut self.options
1095 }
1096}
1097
1098typed_outbound_input!(ScheduleActivityInput);
1099
1100#[non_exhaustive]
1102pub struct ScheduleLocalActivityInput {
1103 activity_type: String,
1104 decoded: DecodedInput,
1105 options: LocalActivityOptions,
1106}
1107
1108impl ScheduleLocalActivityInput {
1109 pub(crate) fn new(
1110 activity_type: String,
1111 input: Box<dyn Any>,
1112 options: LocalActivityOptions,
1113 ) -> Self {
1114 Self {
1115 activity_type,
1116 decoded: DecodedInput::new(Some(input), HashMap::new()),
1117 options,
1118 }
1119 }
1120
1121 pub(crate) fn into_parts(
1122 self,
1123 ) -> (
1124 String,
1125 Box<dyn Any>,
1126 HashMap<String, Payload>,
1127 LocalActivityOptions,
1128 ) {
1129 let (input, headers) = self.decoded.into_parts();
1130 (
1131 self.activity_type,
1132 input.expect("local activity input must exist"),
1133 headers,
1134 self.options,
1135 )
1136 }
1137
1138 pub fn activity_type(&self) -> &str {
1140 &self.activity_type
1141 }
1142
1143 pub fn activity_type_mut(&mut self) -> &mut String {
1145 &mut self.activity_type
1146 }
1147
1148 pub fn options(&self) -> &LocalActivityOptions {
1150 &self.options
1151 }
1152
1153 pub fn options_mut(&mut self) -> &mut LocalActivityOptions {
1155 &mut self.options
1156 }
1157}
1158
1159typed_outbound_input!(ScheduleLocalActivityInput);
1160
1161#[non_exhaustive]
1163pub struct StartChildWorkflowInput {
1164 workflow_type: String,
1165 decoded: DecodedInput,
1166 options: ChildWorkflowOptions,
1167}
1168
1169impl StartChildWorkflowInput {
1170 pub(crate) fn new(
1171 workflow_type: String,
1172 input: Box<dyn Any>,
1173 options: ChildWorkflowOptions,
1174 ) -> Self {
1175 Self {
1176 workflow_type,
1177 decoded: DecodedInput::new(Some(input), HashMap::new()),
1178 options,
1179 }
1180 }
1181
1182 pub(crate) fn into_parts(
1183 self,
1184 ) -> (
1185 String,
1186 Box<dyn Any>,
1187 HashMap<String, Payload>,
1188 ChildWorkflowOptions,
1189 ) {
1190 let (input, headers) = self.decoded.into_parts();
1191 (
1192 self.workflow_type,
1193 input.expect("child workflow input must exist"),
1194 headers,
1195 self.options,
1196 )
1197 }
1198
1199 pub fn workflow_type(&self) -> &str {
1201 &self.workflow_type
1202 }
1203
1204 pub fn workflow_type_mut(&mut self) -> &mut String {
1206 &mut self.workflow_type
1207 }
1208
1209 pub fn options(&self) -> &ChildWorkflowOptions {
1211 &self.options
1212 }
1213
1214 pub fn options_mut(&mut self) -> &mut ChildWorkflowOptions {
1216 &mut self.options
1217 }
1218}
1219
1220typed_outbound_input!(StartChildWorkflowInput);
1221
1222#[derive(Clone, Debug, PartialEq, Eq)]
1224#[non_exhaustive]
1225pub enum SignalWorkflowTarget {
1226 Child {
1228 workflow_id: String,
1230 },
1231 External {
1233 namespace: String,
1235 workflow_id: String,
1237 run_id: Option<String>,
1239 },
1240}
1241
1242#[non_exhaustive]
1244pub struct SignalWorkflowInput {
1245 signal_name: String,
1246 target: SignalWorkflowTarget,
1247 decoded: DecodedInput,
1248 options: SignalWorkflowOptions,
1249}
1250
1251impl SignalWorkflowInput {
1252 pub(crate) fn new(
1253 signal_name: String,
1254 target: SignalWorkflowTarget,
1255 input: Box<dyn Any>,
1256 options: SignalWorkflowOptions,
1257 ) -> Self {
1258 Self {
1259 signal_name,
1260 target,
1261 decoded: DecodedInput::new(Some(input), HashMap::new()),
1262 options,
1263 }
1264 }
1265
1266 #[allow(clippy::type_complexity)]
1267 pub(crate) fn into_parts(
1268 self,
1269 ) -> (
1270 String,
1271 SignalWorkflowTarget,
1272 Box<dyn Any>,
1273 HashMap<String, Payload>,
1274 SignalWorkflowOptions,
1275 ) {
1276 let (input, headers) = self.decoded.into_parts();
1277 (
1278 self.signal_name,
1279 self.target,
1280 input.expect("signal input must exist"),
1281 headers,
1282 self.options,
1283 )
1284 }
1285
1286 pub fn signal_name(&self) -> &str {
1288 &self.signal_name
1289 }
1290
1291 pub fn signal_name_mut(&mut self) -> &mut String {
1293 &mut self.signal_name
1294 }
1295
1296 pub fn target(&self) -> &SignalWorkflowTarget {
1298 &self.target
1299 }
1300
1301 pub fn target_mut(&mut self) -> &mut SignalWorkflowTarget {
1303 &mut self.target
1304 }
1305
1306 pub fn options(&self) -> &SignalWorkflowOptions {
1308 &self.options
1309 }
1310
1311 pub fn options_mut(&mut self) -> &mut SignalWorkflowOptions {
1313 &mut self.options
1314 }
1315}
1316
1317typed_outbound_input!(SignalWorkflowInput);
1318
1319#[derive(Clone, Debug)]
1321#[non_exhaustive]
1322pub struct CancelExternalWorkflowInput {
1323 pub workflow_id: String,
1325 pub run_id: Option<String>,
1327 pub reason: Option<String>,
1329}
1330
1331#[non_exhaustive]
1333pub struct ContinueAsNewInput {
1334 decoded: DecodedInput,
1335 options: ContinueAsNewOptions,
1336}
1337
1338impl ContinueAsNewInput {
1339 pub(crate) fn new(input: Box<dyn Any>, options: ContinueAsNewOptions) -> Self {
1340 Self {
1341 decoded: DecodedInput::new(Some(input), HashMap::new()),
1342 options,
1343 }
1344 }
1345
1346 pub(crate) fn into_parts(
1347 self,
1348 ) -> (Box<dyn Any>, HashMap<String, Payload>, ContinueAsNewOptions) {
1349 let (input, headers) = self.decoded.into_parts();
1350 (
1351 input.expect("continue-as-new input must exist"),
1352 headers,
1353 self.options,
1354 )
1355 }
1356
1357 pub fn options(&self) -> &ContinueAsNewOptions {
1359 &self.options
1360 }
1361
1362 pub fn options_mut(&mut self) -> &mut ContinueAsNewOptions {
1364 &mut self.options
1365 }
1366}
1367
1368typed_outbound_input!(ContinueAsNewInput);
1369
1370pub type ScheduleActivityResult = Result<Box<dyn WorkflowOutboundValue>, ActivityExecutionError>;
1372
1373pub type ChildWorkflowOutboundResult =
1375 Result<Box<dyn WorkflowOutboundValue>, ChildWorkflowExecutionError>;
1376
1377pub type SignalWorkflowResult = Result<(), WorkflowSignalError>;
1379
1380pub type StartChildWorkflowResult = Result<StartChildWorkflowOutput, ChildWorkflowStartError>;
1382
1383pub type ContinueAsNewResult = Result<Infallible, WorkflowTermination>;
1385
1386pub trait WorkflowInterceptor: 'static {
1411 fn initialize_workflow(
1415 &self,
1416 _ctx: WorkflowContextView,
1417 input: InitializeWorkflowInput,
1418 next: WorkflowNext<'_, InitializeWorkflowInput, InitializeWorkflowOutput>,
1419 ) -> InitializeWorkflowOutput {
1420 next.run(input)
1421 }
1422
1423 fn execute<'a>(
1428 &'a self,
1429 _ctx: WorkflowInterceptorContext,
1430 input: ExecuteWorkflowInput,
1431 next: WorkflowNext<
1432 'a,
1433 ExecuteWorkflowInput,
1434 WorkflowInterceptorFuture<'a, ExecuteWorkflowResult>,
1435 >,
1436 ) -> WorkflowInterceptorFuture<'a, ExecuteWorkflowResult> {
1437 next.run(input)
1438 }
1439
1440 fn handle_signal<'a>(
1442 &'a self,
1443 _ctx: WorkflowInterceptorContext,
1444 input: HandleSignalInput,
1445 next: WorkflowNext<
1446 'a,
1447 HandleSignalInput,
1448 WorkflowInterceptorFuture<'a, HandleSignalResult>,
1449 >,
1450 ) -> WorkflowInterceptorFuture<'a, HandleSignalResult> {
1451 next.run(input)
1452 }
1453
1454 fn handle_update<'a>(
1456 &'a self,
1457 _ctx: WorkflowInterceptorContext,
1458 input: HandleUpdateInput,
1459 next: WorkflowNext<
1460 'a,
1461 HandleUpdateInput,
1462 WorkflowInterceptorFuture<'a, HandleUpdateResult>,
1463 >,
1464 ) -> WorkflowInterceptorFuture<'a, HandleUpdateResult> {
1465 next.run(input)
1466 }
1467
1468 fn handle_query(
1470 &self,
1471 _ctx: SyncWorkflowInterceptorContext,
1472 input: HandleQueryInput,
1473 next: WorkflowNext<'_, HandleQueryInput, HandleQueryResult>,
1474 ) -> HandleQueryResult {
1475 next.run(input)
1476 }
1477
1478 fn validate_update(
1480 &self,
1481 _ctx: SyncWorkflowInterceptorContext,
1482 input: ValidateUpdateInput,
1483 next: WorkflowNext<'_, ValidateUpdateInput, ValidateUpdateResult>,
1484 ) -> ValidateUpdateResult {
1485 next.run(input)
1486 }
1487
1488 fn start_timer(
1490 &self,
1491 _ctx: WorkflowInterceptorContext,
1492 input: StartTimerInput,
1493 next: WorkflowNext<
1494 'static,
1495 StartTimerInput,
1496 CancellableWorkflowOutboundFuture<TimerResult>,
1497 >,
1498 ) -> CancellableWorkflowOutboundFuture<TimerResult> {
1499 next.run(input)
1500 }
1501
1502 fn schedule_activity(
1504 &self,
1505 _ctx: WorkflowInterceptorContext,
1506 input: ScheduleActivityInput,
1507 next: WorkflowNext<
1508 'static,
1509 ScheduleActivityInput,
1510 CancellableWorkflowOutboundFuture<ScheduleActivityResult>,
1511 >,
1512 ) -> CancellableWorkflowOutboundFuture<ScheduleActivityResult> {
1513 next.run(input)
1514 }
1515
1516 fn schedule_local_activity(
1518 &self,
1519 _ctx: WorkflowInterceptorContext,
1520 input: ScheduleLocalActivityInput,
1521 next: WorkflowNext<
1522 'static,
1523 ScheduleLocalActivityInput,
1524 CancellableWorkflowOutboundFuture<ScheduleActivityResult>,
1525 >,
1526 ) -> CancellableWorkflowOutboundFuture<ScheduleActivityResult> {
1527 next.run(input)
1528 }
1529
1530 fn start_child_workflow(
1532 &self,
1533 _ctx: WorkflowInterceptorContext,
1534 input: StartChildWorkflowInput,
1535 next: WorkflowNext<
1536 'static,
1537 StartChildWorkflowInput,
1538 CancellableWorkflowOutboundFuture<StartChildWorkflowResult>,
1539 >,
1540 ) -> CancellableWorkflowOutboundFuture<StartChildWorkflowResult> {
1541 next.run(input)
1542 }
1543
1544 fn signal_workflow(
1546 &self,
1547 _ctx: WorkflowInterceptorContext,
1548 input: SignalWorkflowInput,
1549 next: WorkflowNext<
1550 'static,
1551 SignalWorkflowInput,
1552 CancellableWorkflowOutboundFuture<SignalWorkflowResult>,
1553 >,
1554 ) -> CancellableWorkflowOutboundFuture<SignalWorkflowResult> {
1555 next.run(input)
1556 }
1557
1558 fn cancel_external_workflow(
1560 &self,
1561 _ctx: WorkflowInterceptorContext,
1562 input: CancelExternalWorkflowInput,
1563 next: WorkflowNext<
1564 'static,
1565 CancelExternalWorkflowInput,
1566 WorkflowOutboundFuture<CancelExternalWfResult>,
1567 >,
1568 ) -> WorkflowOutboundFuture<CancelExternalWfResult> {
1569 next.run(input)
1570 }
1571
1572 fn continue_as_new(
1574 &self,
1575 _ctx: SyncWorkflowInterceptorContext,
1576 input: ContinueAsNewInput,
1577 next: WorkflowNext<'static, ContinueAsNewInput, ContinueAsNewResult>,
1578 ) -> ContinueAsNewResult {
1579 next.run(input)
1580 }
1581
1582 #[cfg(feature = "experimental")]
1584 fn start_nexus_operation(
1585 &self,
1586 _ctx: WorkflowInterceptorContext,
1587 input: StartNexusOperationInput,
1588 next: WorkflowNext<
1589 'static,
1590 StartNexusOperationInput,
1591 CancellableWorkflowOutboundFuture<StartNexusOperationResult>,
1592 >,
1593 ) -> CancellableWorkflowOutboundFuture<StartNexusOperationResult> {
1594 next.run(input)
1595 }
1596}
1597
1598macro_rules! outbound_chain {
1599 ($fn_name:ident, $method:ident, $context:ty, $input:ty, $output:ty) => {
1600 pub(crate) fn $fn_name(
1601 interceptors: Rc<[Arc<dyn WorkflowInterceptor>]>,
1602 ctx: $context,
1603 input: $input,
1604 next: WorkflowNext<'static, $input, $output>,
1605 ) -> $output {
1606 fn call(
1607 interceptors: Rc<[Arc<dyn WorkflowInterceptor>]>,
1608 interceptor_count: usize,
1609 ctx: $context,
1610 input: $input,
1611 next: WorkflowNext<'static, $input, $output>,
1612 ) -> $output {
1613 if let Some(interceptor_index) = interceptor_count.checked_sub(1) {
1614 let interceptor = interceptors[interceptor_index].clone();
1615 let next_ctx = ctx.clone();
1616 let downstream = WorkflowNext::new(move |input| {
1617 call(interceptors, interceptor_index, next_ctx, input, next)
1618 });
1619 interceptor.$method(ctx, input, downstream)
1620 } else {
1621 next.run(input)
1622 }
1623 }
1624
1625 let interceptor_count = interceptors.len();
1626 call(interceptors, interceptor_count, ctx, input, next)
1627 }
1628 };
1629}
1630
1631#[cfg(feature = "experimental")]
1632mod nexus;
1633
1634outbound_chain!(
1635 call_start_timer,
1636 start_timer,
1637 WorkflowInterceptorContext,
1638 StartTimerInput,
1639 CancellableWorkflowOutboundFuture<TimerResult>
1640);
1641outbound_chain!(
1642 call_schedule_activity,
1643 schedule_activity,
1644 WorkflowInterceptorContext,
1645 ScheduleActivityInput,
1646 CancellableWorkflowOutboundFuture<ScheduleActivityResult>
1647);
1648outbound_chain!(
1649 call_schedule_local_activity,
1650 schedule_local_activity,
1651 WorkflowInterceptorContext,
1652 ScheduleLocalActivityInput,
1653 CancellableWorkflowOutboundFuture<ScheduleActivityResult>
1654);
1655outbound_chain!(
1656 call_start_child_workflow,
1657 start_child_workflow,
1658 WorkflowInterceptorContext,
1659 StartChildWorkflowInput,
1660 CancellableWorkflowOutboundFuture<StartChildWorkflowResult>
1661);
1662outbound_chain!(
1663 call_signal_workflow,
1664 signal_workflow,
1665 WorkflowInterceptorContext,
1666 SignalWorkflowInput,
1667 CancellableWorkflowOutboundFuture<SignalWorkflowResult>
1668);
1669outbound_chain!(
1670 call_cancel_external_workflow,
1671 cancel_external_workflow,
1672 WorkflowInterceptorContext,
1673 CancelExternalWorkflowInput,
1674 WorkflowOutboundFuture<CancelExternalWfResult>
1675);
1676outbound_chain!(
1677 call_continue_as_new,
1678 continue_as_new,
1679 SyncWorkflowInterceptorContext,
1680 ContinueAsNewInput,
1681 ContinueAsNewResult
1682);
1683type WorkflowInterceptorConstructorFn =
1684 dyn Fn(&WorkflowContextView) -> Arc<dyn WorkflowInterceptor> + Send + Sync + 'static;
1685
1686#[derive(Clone)]
1692pub struct WorkflowInterceptorConstructor {
1693 constructor: Arc<WorkflowInterceptorConstructorFn>,
1694}
1695
1696impl WorkflowInterceptorConstructor {
1697 pub fn new<F, I>(constructor: F) -> Self
1699 where
1700 F: Fn(&WorkflowContextView) -> I + Send + Sync + 'static,
1701 I: WorkflowInterceptor,
1702 {
1703 Self {
1704 constructor: Arc::new(move |ctx| Arc::new(constructor(ctx))),
1705 }
1706 }
1707
1708 pub(crate) fn construct(&self, ctx: &WorkflowContextView) -> Arc<dyn WorkflowInterceptor> {
1709 (self.constructor)(ctx)
1710 }
1711}
1712
1713pub(crate) fn wrong_workflow_input_type(type_name: &'static str) -> WorkflowTermination {
1714 WorkflowTermination::failed_application(temporalio_common_wasm::error::ApplicationFailure::new(
1715 anyhow::anyhow!(
1716 "Workflow inbound interceptor returned arguments with wrong concrete type for workflow {type_name}"
1717 ),
1718 ))
1719}
1720
1721#[cfg(test)]
1722mod tests {
1723 use super::*;
1724 use std::cell::Cell;
1725
1726 fn cancellable_future<T: 'static>(
1727 future: impl Future<Output = T> + 'static,
1728 token: &WorkflowCancellationToken,
1729 cancellation_count: &Rc<Cell<usize>>,
1730 ) -> CancellableWorkflowOutboundFuture<T> {
1731 let cancellation_count = cancellation_count.clone();
1732 CancellableWorkflowOutboundFuture::new(
1733 future,
1734 WorkflowCancellationHandle::new(move |_| {
1735 cancellation_count.set(cancellation_count.get() + 1);
1736 }),
1737 )
1738 .with_cancellation_token(token.clone())
1739 }
1740
1741 #[test]
1742 fn pending_cancellable_future_observes_token_cancellation() {
1743 let token = WorkflowCancellationToken::new();
1744 let cancellation_count = Rc::new(Cell::new(0));
1745 let _future = cancellable_future(std::future::pending::<()>(), &token, &cancellation_count);
1746
1747 token.cancel();
1748
1749 assert_eq!(cancellation_count.get(), 1);
1750 }
1751
1752 #[test]
1753 fn completed_cancellable_future_unregisters_from_token() {
1754 let token = WorkflowCancellationToken::new();
1755 let cancellation_count = Rc::new(Cell::new(0));
1756 let future = cancellable_future(std::future::ready(()), &token, &cancellation_count);
1757
1758 assert_eq!(future.now_or_never(), Some(()));
1759 token.cancel();
1760
1761 assert_eq!(cancellation_count.get(), 0);
1762 }
1763
1764 #[test]
1765 fn mapped_cancellable_future_unregisters_from_token() {
1766 let token = WorkflowCancellationToken::new();
1767 let cancellation_count = Rc::new(Cell::new(0));
1768 let future = cancellable_future(std::future::ready(1), &token, &cancellation_count)
1769 .map(|value| value + 1);
1770
1771 assert_eq!(future.now_or_never(), Some(2));
1772 token.cancel();
1773
1774 assert_eq!(cancellation_count.get(), 0);
1775 }
1776
1777 #[test]
1778 fn shared_cancellable_future_unregisters_from_token() {
1779 let token = WorkflowCancellationToken::new();
1780 let cancellation_count = Rc::new(Cell::new(0));
1781 let future =
1782 cancellable_future(std::future::ready(1), &token, &cancellation_count).shared();
1783
1784 assert_eq!(future.clone().now_or_never(), Some(1));
1785 token.cancel();
1786
1787 assert_eq!(cancellation_count.get(), 0);
1788 }
1789}