@temporalio/core-bridge 1.15.0 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/Cargo.lock +172 -70
  2. package/lib/native.d.ts +1 -1
  3. package/package.json +2 -2
  4. package/releases/aarch64-apple-darwin/index.node +0 -0
  5. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  6. package/releases/x86_64-apple-darwin/index.node +0 -0
  7. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  8. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  9. package/sdk-core/.github/workflows/per-pr.yml +6 -6
  10. package/sdk-core/AGENTS.md +41 -30
  11. package/sdk-core/Cargo.toml +3 -0
  12. package/sdk-core/README.md +15 -9
  13. package/sdk-core/crates/client/Cargo.toml +4 -0
  14. package/sdk-core/crates/client/README.md +139 -0
  15. package/sdk-core/crates/client/src/async_activity_handle.rs +297 -0
  16. package/sdk-core/crates/client/src/callback_based.rs +7 -0
  17. package/sdk-core/crates/client/src/errors.rs +294 -0
  18. package/sdk-core/crates/client/src/{raw.rs → grpc.rs} +280 -159
  19. package/sdk-core/crates/client/src/lib.rs +920 -1326
  20. package/sdk-core/crates/client/src/metrics.rs +24 -33
  21. package/sdk-core/crates/client/src/options_structs.rs +457 -0
  22. package/sdk-core/crates/client/src/replaceable.rs +5 -4
  23. package/sdk-core/crates/client/src/request_extensions.rs +8 -9
  24. package/sdk-core/crates/client/src/retry.rs +99 -54
  25. package/sdk-core/crates/client/src/{worker/mod.rs → worker.rs} +1 -1
  26. package/sdk-core/crates/client/src/workflow_handle.rs +826 -0
  27. package/sdk-core/crates/common/Cargo.toml +61 -2
  28. package/sdk-core/crates/common/build.rs +742 -12
  29. package/sdk-core/crates/common/protos/api_upstream/.github/workflows/ci.yml +2 -0
  30. package/sdk-core/crates/common/protos/api_upstream/Makefile +2 -1
  31. package/sdk-core/crates/common/protos/api_upstream/buf.yaml +0 -3
  32. package/sdk-core/crates/common/protos/api_upstream/cmd/check-path-conflicts/main.go +137 -0
  33. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +1166 -770
  34. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +1243 -750
  35. package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +2 -2
  36. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +4 -3
  37. package/sdk-core/crates/common/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -0
  38. package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +4 -0
  39. package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
  40. package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +16 -1
  41. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -6
  42. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +88 -33
  43. package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +4 -2
  44. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
  45. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +5 -5
  46. package/sdk-core/crates/common/src/activity_definition.rs +20 -0
  47. package/sdk-core/crates/common/src/data_converters.rs +770 -0
  48. package/sdk-core/crates/common/src/envconfig.rs +5 -0
  49. package/sdk-core/crates/common/src/lib.rs +15 -211
  50. package/sdk-core/crates/common/src/payload_visitor.rs +648 -0
  51. package/sdk-core/crates/common/src/priority.rs +110 -0
  52. package/sdk-core/crates/common/src/protos/canned_histories.rs +3 -0
  53. package/sdk-core/crates/common/src/protos/history_builder.rs +45 -0
  54. package/sdk-core/crates/common/src/protos/history_info.rs +2 -0
  55. package/sdk-core/crates/common/src/protos/mod.rs +122 -27
  56. package/sdk-core/crates/common/src/protos/task_token.rs +3 -3
  57. package/sdk-core/crates/common/src/protos/utilities.rs +11 -0
  58. package/sdk-core/crates/{sdk-core → common}/src/telemetry/log_export.rs +5 -7
  59. package/sdk-core/crates/common/src/telemetry/metrics/core.rs +125 -0
  60. package/sdk-core/crates/common/src/telemetry/metrics.rs +268 -223
  61. package/sdk-core/crates/{sdk-core → common}/src/telemetry/otel.rs +8 -13
  62. package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_meter.rs +49 -50
  63. package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_server.rs +2 -3
  64. package/sdk-core/crates/common/src/telemetry.rs +264 -4
  65. package/sdk-core/crates/common/src/worker.rs +68 -603
  66. package/sdk-core/crates/common/src/workflow_definition.rs +60 -0
  67. package/sdk-core/crates/macros/Cargo.toml +5 -1
  68. package/sdk-core/crates/macros/src/activities_definitions.rs +585 -0
  69. package/sdk-core/crates/macros/src/fsm_impl.rs +507 -0
  70. package/sdk-core/crates/macros/src/lib.rs +138 -512
  71. package/sdk-core/crates/macros/src/macro_utils.rs +106 -0
  72. package/sdk-core/crates/macros/src/workflow_definitions.rs +1224 -0
  73. package/sdk-core/crates/sdk/Cargo.toml +19 -6
  74. package/sdk-core/crates/sdk/README.md +415 -0
  75. package/sdk-core/crates/sdk/src/activities.rs +417 -0
  76. package/sdk-core/crates/sdk/src/interceptors.rs +1 -1
  77. package/sdk-core/crates/sdk/src/lib.rs +757 -442
  78. package/sdk-core/crates/sdk/src/workflow_context/options.rs +45 -35
  79. package/sdk-core/crates/sdk/src/workflow_context.rs +1033 -289
  80. package/sdk-core/crates/sdk/src/workflow_future.rs +277 -213
  81. package/sdk-core/crates/sdk/src/workflows.rs +711 -0
  82. package/sdk-core/crates/sdk-core/Cargo.toml +57 -64
  83. package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +41 -35
  84. package/sdk-core/crates/sdk-core/machine_coverage/ActivityMachine_Coverage.puml +1 -1
  85. package/sdk-core/crates/sdk-core/src/abstractions.rs +6 -10
  86. package/sdk-core/crates/sdk-core/src/core_tests/activity_tasks.rs +6 -5
  87. package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +13 -15
  88. package/sdk-core/crates/sdk-core/src/core_tests/queries.rs +21 -25
  89. package/sdk-core/crates/sdk-core/src/core_tests/replay_flag.rs +7 -10
  90. package/sdk-core/crates/sdk-core/src/core_tests/updates.rs +14 -17
  91. package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +493 -26
  92. package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +4 -8
  93. package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +7 -7
  94. package/sdk-core/crates/sdk-core/src/histfetch.rs +20 -10
  95. package/sdk-core/crates/sdk-core/src/lib.rs +41 -111
  96. package/sdk-core/crates/sdk-core/src/pollers/mod.rs +4 -9
  97. package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +118 -19
  98. package/sdk-core/crates/sdk-core/src/protosext/mod.rs +2 -2
  99. package/sdk-core/crates/sdk-core/src/replay/mod.rs +14 -5
  100. package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +179 -196
  101. package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +3 -280
  102. package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +6 -9
  103. package/sdk-core/crates/sdk-core/src/test_help/unit_helpers.rs +3 -6
  104. package/sdk-core/crates/sdk-core/src/worker/activities/local_activities.rs +11 -14
  105. package/sdk-core/crates/sdk-core/src/worker/activities.rs +16 -19
  106. package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +9 -5
  107. package/sdk-core/crates/sdk-core/src/worker/client.rs +103 -81
  108. package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +7 -11
  109. package/sdk-core/crates/sdk-core/src/worker/mod.rs +1124 -229
  110. package/sdk-core/crates/sdk-core/src/worker/nexus.rs +145 -23
  111. package/sdk-core/crates/sdk-core/src/worker/slot_provider.rs +2 -2
  112. package/sdk-core/crates/sdk-core/src/worker/tuner/fixed_size.rs +2 -2
  113. package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +13 -13
  114. package/sdk-core/crates/sdk-core/src/worker/tuner.rs +28 -8
  115. package/sdk-core/crates/sdk-core/src/worker/workflow/driven_workflow.rs +9 -3
  116. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +21 -22
  117. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +19 -4
  118. package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +14 -18
  119. package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +4 -6
  120. package/sdk-core/crates/sdk-core/src/worker/workflow/run_cache.rs +4 -7
  121. package/sdk-core/crates/sdk-core/src/worker/workflow/wft_extraction.rs +2 -4
  122. package/sdk-core/crates/sdk-core/src/worker/workflow/wft_poller.rs +8 -9
  123. package/sdk-core/crates/sdk-core/src/worker/workflow/workflow_stream.rs +1 -3
  124. package/sdk-core/crates/sdk-core/tests/activities_procmacro.rs +6 -0
  125. package/sdk-core/crates/sdk-core/tests/activities_trybuild/basic_pass.rs +54 -0
  126. package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.rs +18 -0
  127. package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.stderr +5 -0
  128. package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.rs +14 -0
  129. package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.stderr +5 -0
  130. package/sdk-core/crates/sdk-core/tests/activities_trybuild/multi_arg_pass.rs +48 -0
  131. package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_input_pass.rs +14 -0
  132. package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_return_type_pass.rs +19 -0
  133. package/sdk-core/crates/sdk-core/tests/cloud_tests.rs +14 -5
  134. package/sdk-core/crates/sdk-core/tests/common/activity_functions.rs +55 -0
  135. package/sdk-core/crates/sdk-core/tests/common/mod.rs +241 -196
  136. package/sdk-core/crates/sdk-core/tests/common/workflows.rs +41 -28
  137. package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +3 -5
  138. package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +73 -64
  139. package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +298 -252
  140. package/sdk-core/crates/sdk-core/tests/integ_tests/async_activity_client_tests.rs +230 -0
  141. package/sdk-core/crates/sdk-core/tests/integ_tests/client_tests.rs +94 -57
  142. package/sdk-core/crates/sdk-core/tests/integ_tests/data_converter_tests.rs +381 -0
  143. package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +16 -12
  144. package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +48 -40
  145. package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +327 -255
  146. package/sdk-core/crates/sdk-core/tests/integ_tests/pagination_tests.rs +50 -45
  147. package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +147 -126
  148. package/sdk-core/crates/sdk-core/tests/integ_tests/queries_tests.rs +103 -89
  149. package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +609 -453
  150. package/sdk-core/crates/sdk-core/tests/integ_tests/visibility_tests.rs +80 -62
  151. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +360 -231
  152. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +248 -185
  153. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +52 -43
  154. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_client_tests.rs +180 -0
  155. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +428 -315
  156. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +82 -56
  157. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +56 -28
  158. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +364 -243
  159. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/client_interactions.rs +552 -0
  160. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +101 -42
  161. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +243 -147
  162. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +98 -28
  163. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1475 -1036
  164. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +73 -41
  165. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +397 -238
  166. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +414 -189
  167. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/queries.rs +415 -0
  168. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/replay.rs +96 -36
  169. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +154 -137
  170. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +183 -105
  171. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +85 -38
  172. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +142 -40
  173. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +73 -54
  174. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +363 -226
  175. package/sdk-core/crates/sdk-core/tests/main.rs +17 -15
  176. package/sdk-core/crates/sdk-core/tests/manual_tests.rs +207 -152
  177. package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +65 -34
  178. package/sdk-core/crates/sdk-core/tests/shared_tests/priority.rs +107 -84
  179. package/sdk-core/crates/sdk-core/tests/workflows_procmacro.rs +6 -0
  180. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.rs +26 -0
  181. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.stderr +5 -0
  182. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/basic_pass.rs +49 -0
  183. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/minimal_pass.rs +21 -0
  184. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.rs +26 -0
  185. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.stderr +5 -0
  186. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.rs +21 -0
  187. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.stderr +5 -0
  188. package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +7 -1
  189. package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +14 -14
  190. package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +83 -74
  191. package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +9 -14
  192. package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +1 -2
  193. package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +13 -13
  194. package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +6 -6
  195. package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +3 -4
  196. package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +62 -75
  197. package/sdk-core/rustfmt.toml +2 -1
  198. package/src/client.rs +205 -318
  199. package/src/metrics.rs +22 -30
  200. package/src/runtime.rs +4 -5
  201. package/src/worker.rs +16 -19
  202. package/ts/native.ts +1 -1
  203. package/sdk-core/crates/client/src/workflow_handle/mod.rs +0 -212
  204. package/sdk-core/crates/common/src/errors.rs +0 -85
  205. package/sdk-core/crates/common/tests/worker_task_types_test.rs +0 -129
  206. package/sdk-core/crates/sdk/src/activity_context.rs +0 -238
  207. package/sdk-core/crates/sdk/src/app_data.rs +0 -37
  208. package/sdk-core/crates/sdk-core/tests/integ_tests/activity_functions.rs +0 -5
  209. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +0 -61
@@ -0,0 +1,507 @@
1
+ use proc_macro::TokenStream;
2
+ use quote::{quote, quote_spanned};
3
+ use std::collections::{HashMap, HashSet, hash_map::Entry};
4
+ use syn::{
5
+ Error, Fields, Ident, Token, Type, Variant, Visibility, parenthesized,
6
+ parse::{Parse, ParseStream, Result},
7
+ spanned::Spanned,
8
+ };
9
+
10
+ mod kw {
11
+ syn::custom_keyword!(name);
12
+ syn::custom_keyword!(command);
13
+ syn::custom_keyword!(error);
14
+ syn::custom_keyword!(shared);
15
+ syn::custom_keyword!(shared_state);
16
+ }
17
+
18
+ pub(crate) struct StateMachineDefinition {
19
+ visibility: Visibility,
20
+ name: Ident,
21
+ shared_state_type: Option<Type>,
22
+ command_type: Ident,
23
+ error_type: Ident,
24
+ transitions: Vec<Transition>,
25
+ }
26
+
27
+ impl StateMachineDefinition {
28
+ fn is_final_state(&self, state: &Ident) -> bool {
29
+ // If no transitions go from this state, it's a final state.
30
+ !self.transitions.iter().any(|t| t.from == *state)
31
+ }
32
+ }
33
+
34
+ impl Parse for StateMachineDefinition {
35
+ fn parse(input: ParseStream) -> Result<Self> {
36
+ // Parse visibility if present
37
+ let visibility = input.parse()?;
38
+ // parse the state machine name, command type, and error type
39
+ let (name, command_type, error_type, shared_state_type) = parse_machine_types(input)
40
+ .map_err(|mut e| {
41
+ e.combine(Error::new(
42
+ e.span(),
43
+ "The fsm definition should begin with `name MachineName; command CommandType; \
44
+ error ErrorType;` optionally followed by `shared_state SharedStateType;`",
45
+ ));
46
+ e
47
+ })?;
48
+ // Then the state machine definition is simply a sequence of transitions separated by
49
+ // semicolons
50
+ let transitions = input.parse_terminated(Transition::parse, Token![;])?;
51
+ let transitions: Vec<_> = transitions.into_iter().collect();
52
+ // Check for and whine about any identical transitions. We do this here because preserving
53
+ // the order transitions were defined in is important, so simply collecting to a set is
54
+ // not ideal.
55
+ let trans_set: HashSet<_> = transitions.iter().collect();
56
+ if trans_set.len() != transitions.len() {
57
+ return Err(Error::new(
58
+ input.span(),
59
+ "Duplicate transitions are not allowed!",
60
+ ));
61
+ }
62
+ Ok(Self {
63
+ visibility,
64
+ name,
65
+ shared_state_type,
66
+ command_type,
67
+ error_type,
68
+ transitions,
69
+ })
70
+ }
71
+ }
72
+
73
+ fn parse_machine_types(input: ParseStream) -> Result<(Ident, Ident, Ident, Option<Type>)> {
74
+ let _: kw::name = input.parse()?;
75
+ let name: Ident = input.parse()?;
76
+ input.parse::<Token![;]>()?;
77
+
78
+ let _: kw::command = input.parse()?;
79
+ let command_type: Ident = input.parse()?;
80
+ input.parse::<Token![;]>()?;
81
+
82
+ let _: kw::error = input.parse()?;
83
+ let error_type: Ident = input.parse()?;
84
+ input.parse::<Token![;]>()?;
85
+
86
+ let shared_state_type: Option<Type> = if input.peek(kw::shared_state) {
87
+ let _: kw::shared_state = input.parse()?;
88
+ let typep = input.parse()?;
89
+ input.parse::<Token![;]>()?;
90
+ Some(typep)
91
+ } else {
92
+ None
93
+ };
94
+ Ok((name, command_type, error_type, shared_state_type))
95
+ }
96
+
97
+ #[derive(Debug, Clone, Eq, PartialEq, Hash)]
98
+ struct Transition {
99
+ from: Ident,
100
+ to: Vec<Ident>,
101
+ event: Variant,
102
+ handler: Option<Ident>,
103
+ mutates_shared: bool,
104
+ }
105
+
106
+ impl Parse for Transition {
107
+ fn parse(input: ParseStream) -> Result<Self> {
108
+ // Parse the initial state name
109
+ let from: Ident = input.parse()?;
110
+ // Parse at least one dash
111
+ input.parse::<Token![-]>()?;
112
+ while input.peek(Token![-]) {
113
+ input.parse::<Token![-]>()?;
114
+ }
115
+ // Parse transition information inside parens
116
+ let transition_info;
117
+ parenthesized!(transition_info in input);
118
+ // Get the event variant definition
119
+ let event: Variant = transition_info.parse()?;
120
+ // Reject non-unit or single-item-tuple variants
121
+ match &event.fields {
122
+ Fields::Named(_) => {
123
+ return Err(Error::new(
124
+ event.span(),
125
+ "Struct variants are not supported for events",
126
+ ));
127
+ }
128
+ Fields::Unnamed(uf) => {
129
+ if uf.unnamed.len() != 1 {
130
+ return Err(Error::new(
131
+ event.span(),
132
+ "Only tuple variants with exactly one item are supported for events",
133
+ ));
134
+ }
135
+ }
136
+ Fields::Unit => {}
137
+ }
138
+ // Check if there is an event handler, and parse it
139
+ let (mutates_shared, handler) = if transition_info.peek(Token![,]) {
140
+ transition_info.parse::<Token![,]>()?;
141
+ // Check for mut keyword signifying handler wants to mutate shared state
142
+ let mutates = if transition_info.peek(kw::shared) {
143
+ transition_info.parse::<kw::shared>()?;
144
+ true
145
+ } else {
146
+ false
147
+ };
148
+ (mutates, Some(transition_info.parse()?))
149
+ } else {
150
+ (false, None)
151
+ };
152
+ // Parse at least one dash followed by the "arrow"
153
+ input.parse::<Token![-]>()?;
154
+ while input.peek(Token![-]) {
155
+ input.parse::<Token![-]>()?;
156
+ }
157
+ input.parse::<Token![>]>()?;
158
+ // Parse the destination state
159
+ let to: Ident = input.parse()?;
160
+
161
+ Ok(Self {
162
+ from,
163
+ event,
164
+ handler,
165
+ to: vec![to],
166
+ mutates_shared,
167
+ })
168
+ }
169
+ }
170
+
171
+ impl StateMachineDefinition {
172
+ pub(crate) fn codegen(&self) -> TokenStream {
173
+ let visibility = self.visibility.clone();
174
+ // First extract all of the states into a set, and build the enum's insides
175
+ let states = self.all_states();
176
+ let state_variants = states.iter().map(|s| {
177
+ let statestr = s.to_string();
178
+ quote! {
179
+ #[display(#statestr)]
180
+ #s(#s)
181
+ }
182
+ });
183
+ let name = &self.name;
184
+ let name_str = &self.name.to_string();
185
+
186
+ let transition_result_name = Ident::new(&format!("{name}Transition"), name.span());
187
+ let transition_type_alias = quote! {
188
+ type #transition_result_name<Ds, Sm = #name> = TransitionResult<Sm, Ds>;
189
+ };
190
+
191
+ let state_enum_name = Ident::new(&format!("{name}State"), name.span());
192
+ // If user has not defined any shared state, use the unit type.
193
+ let shared_state_type = self
194
+ .shared_state_type
195
+ .clone()
196
+ .unwrap_or_else(|| syn::parse_str("()").unwrap());
197
+ let machine_struct = quote! {
198
+ #[derive(Clone)]
199
+ #visibility struct #name {
200
+ state: ::core::option::Option<#state_enum_name>,
201
+ shared_state: #shared_state_type
202
+ }
203
+ };
204
+ let states_enum = quote! {
205
+ #[derive(::derive_more::From, Clone, ::derive_more::Display)]
206
+ #visibility enum #state_enum_name {
207
+ #(#state_variants),*
208
+ }
209
+ };
210
+ let state_is_final_match_arms = states.iter().map(|s| {
211
+ let val = if self.is_final_state(s) {
212
+ quote! { true }
213
+ } else {
214
+ quote! { false }
215
+ };
216
+ quote! { #state_enum_name::#s(_) => #val }
217
+ });
218
+ let states_enum_impl = quote! {
219
+ impl #state_enum_name {
220
+ fn is_final(&self) -> bool {
221
+ match self {
222
+ #(#state_is_final_match_arms),*
223
+ }
224
+ }
225
+ }
226
+ };
227
+
228
+ // Build the events enum
229
+ let events: HashSet<Variant> = self.transitions.iter().map(|t| t.event.clone()).collect();
230
+ let events_enum_name = Ident::new(&format!("{name}Events"), name.span());
231
+ let events: Vec<_> = events
232
+ .into_iter()
233
+ .map(|v| {
234
+ let vname = v.ident.to_string();
235
+ quote! {
236
+ #[display(#vname)]
237
+ #v
238
+ }
239
+ })
240
+ .collect();
241
+ let events_enum = quote! {
242
+ #[derive(::derive_more::Display)]
243
+ #visibility enum #events_enum_name {
244
+ #(#events),*
245
+ }
246
+ };
247
+
248
+ // Construct the trait implementation
249
+ let cmd_type = &self.command_type;
250
+ let err_type = &self.error_type;
251
+ let mut statemap: HashMap<Ident, Vec<Transition>> = HashMap::new();
252
+ for t in &self.transitions {
253
+ statemap
254
+ .entry(t.from.clone())
255
+ .and_modify(|v| v.push(t.clone()))
256
+ .or_insert_with(|| vec![t.clone()]);
257
+ }
258
+ // Add any states without any transitions to the map
259
+ for s in &states {
260
+ if !statemap.contains_key(s) {
261
+ statemap.insert(s.clone(), vec![]);
262
+ }
263
+ }
264
+ let transition_result_transform = quote! {
265
+ match res.into_cmd_result() {
266
+ Ok((cmds, state)) => {
267
+ self.state = Some(state);
268
+ Ok(cmds)
269
+ }
270
+ Err(e) => Err(e)
271
+ }
272
+ };
273
+ let mut multi_dest_enums = vec![];
274
+ let mut multi_dest_enum_names = HashSet::new();
275
+ let state_branches: Vec<_> = statemap.into_iter().map(|(from, transitions)| {
276
+ let occupied_current_state = quote! { Some(#state_enum_name::#from(state_data)) };
277
+ // Merge transition dest states with the same handler
278
+ let transitions = merge_transition_dests(transitions);
279
+ let event_branches = transitions
280
+ .into_iter()
281
+ .map(|ts| {
282
+ let ev_variant = &ts.event.ident;
283
+ if let Some(ts_fn) = ts.handler.clone() {
284
+ let span = ts_fn.span();
285
+ let trans_type = match ts.to.as_slice() {
286
+ [] => unreachable!("There will be at least one dest state in transitions"),
287
+ [one_to] => quote! {
288
+ #transition_result_name<#one_to>
289
+ },
290
+ multi_dests => {
291
+ let string_dests: Vec<_> = multi_dests.iter()
292
+ .map(ToString::to_string).collect();
293
+ let enum_ident = Ident::new(&string_dests.join("Or"),
294
+ multi_dests[0].span());
295
+ let multi_dest_enum = quote! {
296
+ #[derive(::derive_more::From)]
297
+ #visibility enum #enum_ident {
298
+ #(#multi_dests(#multi_dests)),*
299
+ }
300
+ impl ::core::convert::From<#enum_ident> for #state_enum_name {
301
+ fn from(v: #enum_ident) -> Self {
302
+ match v {
303
+ #( #enum_ident::#multi_dests(sv) =>
304
+ Self::#multi_dests(sv) ),*
305
+ }
306
+ }
307
+ }
308
+ };
309
+ // Deduplicate; two different events may each result in a transition
310
+ // set with the same set of dest states
311
+ if multi_dest_enum_names.insert(enum_ident.clone()) {
312
+ multi_dest_enums.push(multi_dest_enum);
313
+ }
314
+ quote! {
315
+ #transition_result_name<#enum_ident>
316
+ }
317
+ }
318
+ };
319
+ match ts.event.fields {
320
+ Fields::Unnamed(_) => {
321
+ let arglist = if ts.mutates_shared {
322
+ quote! {&mut self.shared_state, val}
323
+ } else {
324
+ quote! {val}
325
+ };
326
+ quote_spanned! { span =>
327
+ #events_enum_name::#ev_variant(val) => {
328
+ let res: #trans_type = state_data.#ts_fn(#arglist);
329
+ #transition_result_transform
330
+ }
331
+ }
332
+ }
333
+ Fields::Unit => {
334
+ let arglist = if ts.mutates_shared {
335
+ quote! {&mut self.shared_state}
336
+ } else {
337
+ quote! {}
338
+ };
339
+ quote_spanned! { span =>
340
+ #events_enum_name::#ev_variant => {
341
+ let res: #trans_type = state_data.#ts_fn(#arglist);
342
+ #transition_result_transform
343
+ }
344
+ }
345
+ }
346
+ Fields::Named(_) => unreachable!(),
347
+ }
348
+ } else {
349
+ // If events do not have a handler, attempt to construct the next state
350
+ // using `Default`.
351
+ if let [new_state] = ts.to.as_slice() {
352
+ let span = new_state.span();
353
+ let default_trans = quote_spanned! { span =>
354
+ let res = TransitionResult::<Self, #new_state>::from::<#from>(state_data);
355
+ #transition_result_transform
356
+ };
357
+ let span = ts.event.span();
358
+ match ts.event.fields {
359
+ Fields::Unnamed(_) => quote_spanned! { span =>
360
+ #events_enum_name::#ev_variant(_val) => {
361
+ #default_trans
362
+ }
363
+ },
364
+ Fields::Unit => quote_spanned! { span =>
365
+ #events_enum_name::#ev_variant => {
366
+ #default_trans
367
+ }
368
+ },
369
+ Fields::Named(_) => unreachable!(),
370
+ }
371
+ } else {
372
+ unreachable!("It should be impossible to have more than one dest state \
373
+ in no-handler transitions")
374
+ }
375
+ }
376
+ })
377
+ // Since most states won't handle every possible event, return an error to that
378
+ // effect
379
+ .chain(std::iter::once(
380
+ quote! { _ => {
381
+ // Restore state in event the transition doesn't match
382
+ self.state = #occupied_current_state;
383
+ return Err(::temporalio_common::fsm_trait::MachineError::InvalidTransition)
384
+ } },
385
+ ));
386
+ quote! {
387
+ #occupied_current_state => match event {
388
+ #(#event_branches),*
389
+ }
390
+ }
391
+ }).chain(std::iter::once(
392
+ quote! {
393
+ None => Err(::temporalio_common::fsm_trait::MachineError::InvalidTransition)
394
+ }
395
+ )).collect();
396
+
397
+ let viz_str = self.visualize();
398
+
399
+ let trait_impl = quote! {
400
+ impl ::temporalio_common::fsm_trait::StateMachine for #name {
401
+ type Error = #err_type;
402
+ type State = #state_enum_name;
403
+ type SharedState = #shared_state_type;
404
+ type Event = #events_enum_name;
405
+ type Command = #cmd_type;
406
+
407
+ fn name(&self) -> &str {
408
+ #name_str
409
+ }
410
+
411
+ fn on_event(&mut self, event: #events_enum_name)
412
+ -> ::core::result::Result<::std::vec::Vec<Self::Command>,
413
+ ::temporalio_common::fsm_trait::MachineError<Self::Error>> {
414
+ let taken_state = self.state.take();
415
+ match taken_state {
416
+ #(#state_branches),*
417
+ }
418
+ }
419
+
420
+ fn state(&self) -> &Self::State {
421
+ self.state.as_ref().unwrap()
422
+ }
423
+
424
+ fn shared_state(&self) -> &Self::SharedState{
425
+ &self.shared_state
426
+ }
427
+
428
+ fn has_reached_final_state(&self) -> bool {
429
+ self.state.as_ref().unwrap().is_final()
430
+ }
431
+
432
+ fn from_parts(state: Self::State, shared: Self::SharedState) -> Self {
433
+ Self { shared_state: shared, state: Some(state) }
434
+ }
435
+
436
+ fn visualizer() -> &'static str {
437
+ #viz_str
438
+ }
439
+ }
440
+ };
441
+
442
+ let output = quote! {
443
+ #transition_type_alias
444
+ #machine_struct
445
+ #states_enum
446
+ #(#multi_dest_enums)*
447
+ #states_enum_impl
448
+ #events_enum
449
+ #trait_impl
450
+ };
451
+
452
+ TokenStream::from(output)
453
+ }
454
+
455
+ fn all_states(&self) -> HashSet<Ident> {
456
+ self.transitions
457
+ .iter()
458
+ .flat_map(|t| {
459
+ let mut states = t.to.clone();
460
+ states.push(t.from.clone());
461
+ states
462
+ })
463
+ .collect()
464
+ }
465
+
466
+ fn visualize(&self) -> String {
467
+ let transitions: Vec<String> = self
468
+ .transitions
469
+ .iter()
470
+ .flat_map(|t| {
471
+ t.to.iter()
472
+ .map(move |d| format!("{} --> {}: {}", t.from, d, t.event.ident))
473
+ })
474
+ // Add all final state transitions
475
+ .chain(
476
+ self.all_states()
477
+ .iter()
478
+ .filter(|s| self.is_final_state(s))
479
+ .map(|s| format!("{s} --> [*]")),
480
+ )
481
+ .collect();
482
+ let transitions = transitions.join("\n");
483
+ format!("@startuml\n{transitions}\n@enduml")
484
+ }
485
+ }
486
+
487
+ /// Merge transition's dest state lists for those with the same from state & handler
488
+ fn merge_transition_dests(transitions: Vec<Transition>) -> Vec<Transition> {
489
+ let mut map = HashMap::<_, Transition>::new();
490
+ for t in transitions {
491
+ // We want to use the transition sans-destinations as the key
492
+ let without_dests = {
493
+ let mut wd = t.clone();
494
+ wd.to = vec![];
495
+ wd
496
+ };
497
+ match map.entry(without_dests) {
498
+ Entry::Occupied(mut e) => {
499
+ e.get_mut().to.extend(t.to.into_iter());
500
+ }
501
+ Entry::Vacant(v) => {
502
+ v.insert(t);
503
+ }
504
+ }
505
+ }
506
+ map.into_values().collect()
507
+ }