1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
use TokenStream;
use parse_macro_input;
/// Can be used to define Activities for invocation and execution. Using this macro requires that
/// you also depend on the `temporalio_sdk` crate.
///
/// For a usage example, see that crate's documentation.
/// Marks a method within an `#[activities]` impl block as an activity.
/// This attribute is processed by the `#[activities]` macro and should not be used standalone.
/// Marks a struct as a workflow definition.
///
/// This attribute can optionally specify a custom workflow name:
/// `#[workflow(name = "my-custom-workflow")]`
///
/// If no name is specified, the struct name is used as the workflow type name.
///
/// This attribute must be used in conjunction with `#[workflow_methods]` on an impl block.
/// Defines workflow methods for a workflow struct. Using this macro requires that
/// you also depend on the `temporalio_sdk` crate.
///
/// This macro processes an impl block and generates:
/// - Marker structs for each workflow method
/// - Trait implementations for workflow definition and execution
/// - Registration code for workers
///
/// ## Macro Attributes
///
/// - `factory_only` - When set, the workflow must be registered using
/// `register_workflow_with_factory` and does not need to implement `Default` or define an `#[init]`
/// method. Ex: `#[workflow_methods(factory_only)]`
///
/// ## Method Attributes
///
/// - `#[init]` - Optional initialization method. Signature: `fn new(input: T, ctx: &WorkflowContext) -> Self`
/// - `#[run]` - Required main workflow function. Signature: `async fn run(&mut self, ctx: &mut WorkflowContext) -> WorkflowResult<T>`
/// - `#[signal]` - Signal handler. Sync: `fn signal(&mut self, ctx: &mut SyncWorkflowContext, input: T)`. Async: `async fn signal(ctx: &mut WorkflowContext, input: T)`
/// - `#[query]` - Query handler. Signature: `fn query(&self, ctx: &WorkflowContextView, input: T) -> R` (must NOT be async)
/// - `#[update]` - Update handler. Sync: `fn update(&mut self, ctx: &mut SyncWorkflowContext, input: T) -> R`. Async: `async fn update(ctx: &mut WorkflowContext, input: T) -> R`
///
/// For a usage example, see the `temporalio_sdk` crate's documentation.
/// Marks a method within a `#[workflow_methods]` impl block as the initialization method.
/// This attribute is processed by the `#[workflow_methods]` macro and should not be used standalone.
/// Marks a method within a `#[workflow_methods]` impl block as the main run method.
/// This attribute is processed by the `#[workflow_methods]` macro and should not be used standalone.
/// Marks a method within a `#[workflow_methods]` impl block as a signal handler.
/// This attribute is processed by the `#[workflow_methods]` macro and should not be used standalone.
///
/// Supports an optional `name` parameter to override the signal name:
/// `#[signal(name = "my_signal")]`
/// Marks a method within a `#[workflow_methods]` impl block as a query handler.
/// This attribute is processed by the `#[workflow_methods]` macro and should not be used standalone.
///
/// Supports an optional `name` parameter to override the query name:
/// `#[query(name = "my_query")]`
/// Marks a method within a `#[workflow_methods]` impl block as an update handler.
/// This attribute is processed by the `#[workflow_methods]` macro and should not be used standalone.
///
/// Supports an optional `name` parameter to override the update name:
/// `#[update(name = "my_update")]`
/// Marks a method within a `#[workflow_methods]` impl block as a validator for an update handler.
/// This attribute is processed by the `#[workflow_methods]` macro and should not be used standalone.
///
/// The parameter specifies which update this validator applies to:
/// `#[update_validator(my_update)]`
///
/// The validator method must:
/// - Take `&self` (not `&mut self`)
/// - Take `&WorkflowContextView` as the first parameter
/// - Take a reference to the update's input type as the second parameter
/// - Return `Result<(), Box<dyn std::error::Error + Send + Sync>>`
/// Parses a DSL for defining finite state machines, and produces code implementing the
/// [StateMachine](trait.StateMachine.html) trait.
///
/// An example state machine definition of a card reader for unlocking a door:
/// ```
/// use std::convert::Infallible;
/// use temporalio_common::fsm_trait::{StateMachine, TransitionResult};
/// use temporalio_macros::fsm;
///
/// fsm! {
/// name CardReader; command Commands; error Infallible; shared_state SharedState;
///
/// Locked --(CardReadable(CardData), shared on_card_readable) --> ReadingCard;
/// Locked --(CardReadable(CardData), shared on_card_readable) --> Locked;
/// ReadingCard --(CardAccepted, on_card_accepted) --> DoorOpen;
/// ReadingCard --(CardRejected, on_card_rejected) --> Locked;
/// DoorOpen --(DoorClosed, on_door_closed) --> Locked;
/// }
///
/// #[derive(Clone)]
/// pub struct SharedState {
/// last_id: Option<String>,
/// }
///
/// #[derive(Debug, Clone, Eq, PartialEq, Hash)]
/// pub enum Commands {
/// StartBlinkingLight,
/// StopBlinkingLight,
/// ProcessData(CardData),
/// }
///
/// type CardData = String;
///
/// /// Door is locked / idle / we are ready to read
/// #[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
/// pub struct Locked {}
///
/// /// Actively reading the card
/// #[derive(Debug, Clone, Eq, PartialEq, Hash)]
/// pub struct ReadingCard {
/// card_data: CardData,
/// }
///
/// /// The door is open, we shouldn't be accepting cards and should be blinking the light
/// #[derive(Debug, Clone, Eq, PartialEq, Hash)]
/// pub struct DoorOpen {}
/// impl DoorOpen {
/// fn on_door_closed(&self) -> CardReaderTransition<Locked> {
/// TransitionResult::ok(vec![], Locked {})
/// }
/// }
///
/// impl Locked {
/// fn on_card_readable(
/// &self,
/// shared_dat: &mut SharedState,
/// data: CardData,
/// ) -> CardReaderTransition<ReadingCardOrLocked> {
/// match &shared_dat.last_id {
/// // Arbitrarily deny the same person entering twice in a row
/// Some(d) if d == &data => TransitionResult::ok(vec![], Locked {}.into()),
/// _ => {
/// // Otherwise issue a processing command. This illustrates using the same handler
/// // for different destinations
/// shared_dat.last_id = Some(data.clone());
/// TransitionResult::ok(
/// vec![
/// Commands::ProcessData(data.clone()),
/// Commands::StartBlinkingLight,
/// ],
/// ReadingCard { card_data: data }.into(),
/// )
/// }
/// }
/// }
/// }
///
/// impl ReadingCard {
/// fn on_card_accepted(&self) -> CardReaderTransition<DoorOpen> {
/// TransitionResult::ok(vec![Commands::StopBlinkingLight], DoorOpen {})
/// }
/// fn on_card_rejected(&self) -> CardReaderTransition<Locked> {
/// TransitionResult::ok(vec![Commands::StopBlinkingLight], Locked {})
/// }
/// }
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let crs = CardReaderState::Locked(Locked {});
/// let mut cr = CardReader::from_parts(crs, SharedState { last_id: None });
/// let cmds = cr.on_event(CardReaderEvents::CardReadable("badguy".to_string()))?;
/// assert_eq!(cmds[0], Commands::ProcessData("badguy".to_string()));
/// assert_eq!(cmds[1], Commands::StartBlinkingLight);
///
/// let cmds = cr.on_event(CardReaderEvents::CardRejected)?;
/// assert_eq!(cmds[0], Commands::StopBlinkingLight);
///
/// let cmds = cr.on_event(CardReaderEvents::CardReadable("goodguy".to_string()))?;
/// assert_eq!(cmds[0], Commands::ProcessData("goodguy".to_string()));
/// assert_eq!(cmds[1], Commands::StartBlinkingLight);
///
/// let cmds = cr.on_event(CardReaderEvents::CardAccepted)?;
/// assert_eq!(cmds[0], Commands::StopBlinkingLight);
/// # Ok(())
/// # }
/// ```
///
/// In the above example the first word is the name of the state machine, then after the comma the
/// type (which you must define separately) of commands produced by the machine.
///
/// then each line represents a transition, where the first word is the initial state, the tuple
/// inside the arrow is `(eventtype[, event handler])`, and the word after the arrow is the
/// destination state. here `eventtype` is an enum variant , and `event_handler` is a function you
/// must define outside the enum whose form depends on the event variant. the only variant types
/// allowed are unit and one-item tuple variants. For unit variants, the function takes no
/// parameters. For the tuple variants, the function takes the variant data as its parameter. In
/// either case the function is expected to return a `TransitionResult` to the appropriate state.
///
/// The first transition can be interpreted as "If the machine is in the locked state, when a
/// `CardReadable` event is seen, call `on_card_readable` (passing in `CardData`) and transition to
/// the `ReadingCard` state.
///
/// The macro will generate a few things:
/// * A struct for the overall state machine, named with the provided name. Here:
/// ```text
/// struct CardReader {
/// state: CardReaderState,
/// shared_state: SharedState,
/// }
/// ```
/// * An enum with a variant for each state, named with the provided name + "State".
/// ```text
/// enum CardReaderState {
/// Locked(Locked),
/// ReadingCard(ReadingCard),
/// DoorOpen(DoorOpen),
/// }
/// ```
///
/// You are expected to define a type for each state, to contain that state's data. If there is
/// no data, you can simply: `type StateName = ()`
/// * For any instance of transitions with the same event/handler which transition to different
/// destination states (dynamic destinations), an enum named like `DestAOrDestBOrDestC` is
/// generated. This enum must be used as the destination "state" from those handlers.
/// * An enum with a variant for each event. You are expected to define the type (if any) contained
/// in the event variant.
/// ```text
/// enum CardReaderEvents {
/// DoorClosed,
/// CardAccepted,
/// CardRejected,
/// CardReadable(CardData),
/// }
/// ```
/// * An implementation of the [StateMachine](trait.StateMachine.html) trait for the generated state
/// machine enum (in this case, `CardReader`)
/// * A type alias for a [TransitionResult](enum.TransitionResult.html) with the appropriate generic
/// parameters set for your machine. It is named as your machine with `Transition` appended. In
/// this case, `CardReaderTransition`.