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