1
+ //! This needs to be its own crate so that it doesn't pull in anything that would make compiling
2
+ //! to WASM not work. I've already figured out how to do all that once before with my WASM workflows
3
+ //! hackathon
4
+
1
5
use futures:: future:: BoxFuture ;
2
- use std:: { error:: Error , time:: Duration } ;
3
- use temporal_sdk_core_protos:: temporal:: api:: common:: v1:: Payload ;
6
+ use std:: time:: Duration ;
7
+ use temporal_sdk_core_protos:: {
8
+ coresdk:: workflow_commands:: ContinueAsNewWorkflowExecution , temporal:: api:: common:: v1:: Payload ,
9
+ } ;
10
+
11
+ // anyhow errors are used for the errors returned by user-defined functions. This makes `?` work
12
+ // well everywhere by default which is a very nice property, as well as preserving backtraces. We
13
+ // may need to define our own error type instead to allow attaching things like the non-retryable
14
+ // flag... but I suspect we can just make downcasting work for that.
4
15
5
16
/// Workflow authors must implement this trait to create Temporal Rust workflows
6
17
pub trait Workflow : Sized {
@@ -22,7 +33,10 @@ pub trait Workflow: Sized {
22
33
///
23
34
/// `ctx` should be used to perform various Temporal commands like starting timers and
24
35
/// activities.
25
- fn run ( & mut self , ctx : WfContext ) -> BoxFuture < Self :: Output > ;
36
+ fn run (
37
+ & mut self ,
38
+ ctx : WfContext ,
39
+ ) -> BoxFuture < Result < WfExitValue < Self :: Output > , anyhow:: Error > > ;
26
40
27
41
/// All signals this workflow can handle. Typically you won't implement this directly, it will
28
42
/// automatically contain all signals defined with the `#[signal]` attribute.
@@ -38,13 +52,30 @@ pub trait Workflow: Sized {
38
52
}
39
53
}
40
54
55
+ /// TODO: Exists in SDK in slightly different form, and would move into this crate
56
+ #[ derive( Debug ) ]
57
+ pub enum WfExitValue < T : TemporalSerializable > {
58
+ /// Continue the workflow as a new execution
59
+ ContinueAsNew ( Box < ContinueAsNewWorkflowExecution > ) , // Wouldn't be raw proto in reality
60
+ /// Confirm the workflow was cancelled
61
+ Cancelled ,
62
+ /// Finish with a result
63
+ Normal ( T ) ,
64
+ }
65
+ impl < T : TemporalSerializable > From < T > for WfExitValue < T > {
66
+ fn from ( v : T ) -> Self {
67
+ Self :: Normal ( v)
68
+ }
69
+ }
70
+ // ... also convenience functions for constructing C-A-N, etc.
71
+
41
72
/// A workflow context which contains only information, but does not allow any commands to
42
73
/// be created.
43
74
pub struct SafeWfContext {
44
75
// TODO
45
76
}
46
77
47
- /// TODO: Placeholder, move from SDK
78
+ /// TODO: Placeholder, exists in SDK and would move into this crate & (likely) become a trait
48
79
pub struct WfContext { }
49
80
impl WfContext {
50
81
pub async fn timer ( & self , _: Duration ) {
@@ -53,26 +84,25 @@ impl WfContext {
53
84
}
54
85
55
86
pub struct SignalDefinition < WF : Workflow > {
56
- // TODO: Could be a matching predicate
87
+ // TODO: Could be a matching predicate, to allow for dynamic registration
57
88
name : String ,
58
89
// The handler input type must be erased here, since otherwise we couldn't store/return the
59
90
// heterogeneous collection of definition types in the workflow itself. The signal macro
60
- // will wrap the user's function with code that performs deserialization, as well as error
61
- // boxing.
62
- handler : Box < dyn FnMut ( & mut WF , Payload ) -> Result < ( ) , Box < dyn Error > > > ,
91
+ // will wrap the user's function with code that performs deserialization.
92
+ handler : Box < dyn FnMut ( & mut WF , Payload ) -> Result < ( ) , anyhow:: Error > > ,
63
93
}
64
94
pub struct QueryDefinition < WF : Workflow > {
65
- // TODO: Could be a matching predicate
95
+ // TODO: Could be a matching predicate, to allow for dynamic registration
66
96
name : String ,
67
97
// The query macro will wrap the user's function with code that performs deserialization of
68
98
// input and serialization of output, as well as error boxing.
69
- handler : Box < dyn FnMut ( & WF , Payload ) -> Result < Payload , Box < dyn Error > > > ,
99
+ handler : Box < dyn FnMut ( & WF , Payload ) -> Result < Payload , anyhow :: Error > > ,
70
100
}
71
101
72
- /// TODO: Placeholder, move from (and improve in) SDK
102
+ /// TODO: Placeholders, likely belong inside protos crate. These will be auto-implemented for
103
+ /// anything using serde already (which I expect is how virtually everyone will do this).
73
104
pub trait TemporalSerializable { }
74
105
impl < T > TemporalSerializable for T { }
75
- /// TODO: Placeholder, move from (and improve in) SDK
76
106
pub trait TemporalDeserializable { }
77
107
impl < T > TemporalDeserializable for T { }
78
108
@@ -99,11 +129,18 @@ mod tests {
99
129
Self { foo : 0 , bar }
100
130
}
101
131
102
- fn run ( & mut self , ctx : WfContext ) -> BoxFuture < Self :: Output > {
132
+ fn run (
133
+ & mut self ,
134
+ ctx : WfContext ,
135
+ ) -> BoxFuture < Result < WfExitValue < Self :: Output > , anyhow:: Error > > {
103
136
async move {
104
137
ctx. timer ( Duration :: from_secs ( 1 ) ) . await ;
105
138
self . foo = 1 ;
106
- self . foo
139
+ // The into() is unfortunately unavoidable without making C-A-N and confirm cancel
140
+ // be errors instead. Personally, I don't love that and I think it's not idiomatic
141
+ // Rust, whereas needing to `into()` something is. Other way would be macros, but
142
+ // it's slightly too much magic I think.
143
+ Ok ( self . foo . into ( ) )
107
144
}
108
145
. boxed ( )
109
146
// TODO: The need to box here is slightly unfortunate, but it's either that or require
@@ -114,7 +151,7 @@ mod tests {
114
151
115
152
// #[workflow] miiiight be necessary here, but, ideally is not.
116
153
impl MyWorkflow {
117
- // Attrib commented out since nonexistent for now, but that's what it'd look like.
154
+ // Attrib commented out since it's nonexistent for now, but that's what it'd look like.
118
155
// #[signal]
119
156
pub fn my_signal ( & mut self , arg : String ) {
120
157
self . bar . insert ( arg, 1 ) ;
@@ -139,7 +176,7 @@ mod tests {
139
176
}
140
177
impl MyWorkflowClientExtension for WorkflowHandle < MyWorkflow > {
141
178
fn my_signal ( & self , arg : String ) -> BoxFuture < Result < ( ) , SignalError > > {
142
- // Is actually something like:
179
+ // Becomes something like:
143
180
// self.signal("my_signal", arg.serialize())
144
181
todo ! ( )
145
182
}
@@ -157,4 +194,7 @@ mod tests {
157
194
} ;
158
195
let _ = wfh. my_signal ( "hi!" . to_string ( ) ) . await ;
159
196
}
197
+
198
+ #[ test]
199
+ fn compile ( ) { }
160
200
}
0 commit comments