Skip to content

Commit 03ea804

Browse files
committed
WfExitValue
1 parent 3086581 commit 03ea804

File tree

2 files changed

+57
-16
lines changed

2 files changed

+57
-16
lines changed

workflow-api/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ categories = ["development-tools"]
1313
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1414

1515
[dependencies]
16+
anyhow = "1.0"
1617
futures = "0.3"
1718

1819
[dependencies.temporal-sdk-core-protos]

workflow-api/src/lib.rs

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
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+
15
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.
415

516
/// Workflow authors must implement this trait to create Temporal Rust workflows
617
pub trait Workflow: Sized {
@@ -22,7 +33,10 @@ pub trait Workflow: Sized {
2233
///
2334
/// `ctx` should be used to perform various Temporal commands like starting timers and
2435
/// 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>>;
2640

2741
/// All signals this workflow can handle. Typically you won't implement this directly, it will
2842
/// automatically contain all signals defined with the `#[signal]` attribute.
@@ -38,13 +52,30 @@ pub trait Workflow: Sized {
3852
}
3953
}
4054

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+
4172
/// A workflow context which contains only information, but does not allow any commands to
4273
/// be created.
4374
pub struct SafeWfContext {
4475
// TODO
4576
}
4677

47-
/// TODO: Placeholder, move from SDK
78+
/// TODO: Placeholder, exists in SDK and would move into this crate & (likely) become a trait
4879
pub struct WfContext {}
4980
impl WfContext {
5081
pub async fn timer(&self, _: Duration) {
@@ -53,26 +84,25 @@ impl WfContext {
5384
}
5485

5586
pub struct SignalDefinition<WF: Workflow> {
56-
// TODO: Could be a matching predicate
87+
// TODO: Could be a matching predicate, to allow for dynamic registration
5788
name: String,
5889
// The handler input type must be erased here, since otherwise we couldn't store/return the
5990
// 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>>,
6393
}
6494
pub struct QueryDefinition<WF: Workflow> {
65-
// TODO: Could be a matching predicate
95+
// TODO: Could be a matching predicate, to allow for dynamic registration
6696
name: String,
6797
// The query macro will wrap the user's function with code that performs deserialization of
6898
// 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>>,
70100
}
71101

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).
73104
pub trait TemporalSerializable {}
74105
impl<T> TemporalSerializable for T {}
75-
/// TODO: Placeholder, move from (and improve in) SDK
76106
pub trait TemporalDeserializable {}
77107
impl<T> TemporalDeserializable for T {}
78108

@@ -99,11 +129,18 @@ mod tests {
99129
Self { foo: 0, bar }
100130
}
101131

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>> {
103136
async move {
104137
ctx.timer(Duration::from_secs(1)).await;
105138
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())
107144
}
108145
.boxed()
109146
// TODO: The need to box here is slightly unfortunate, but it's either that or require
@@ -114,7 +151,7 @@ mod tests {
114151

115152
// #[workflow] miiiight be necessary here, but, ideally is not.
116153
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.
118155
// #[signal]
119156
pub fn my_signal(&mut self, arg: String) {
120157
self.bar.insert(arg, 1);
@@ -139,7 +176,7 @@ mod tests {
139176
}
140177
impl MyWorkflowClientExtension for WorkflowHandle<MyWorkflow> {
141178
fn my_signal(&self, arg: String) -> BoxFuture<Result<(), SignalError>> {
142-
// Is actually something like:
179+
// Becomes something like:
143180
// self.signal("my_signal", arg.serialize())
144181
todo!()
145182
}
@@ -157,4 +194,7 @@ mod tests {
157194
};
158195
let _ = wfh.my_signal("hi!".to_string()).await;
159196
}
197+
198+
#[test]
199+
fn compile() {}
160200
}

0 commit comments

Comments
 (0)