@temporalio/core-bridge 1.15.0 → 1.16.0
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.
- package/Cargo.lock +172 -70
- package/lib/native.d.ts +1 -1
- package/package.json +2 -2
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/sdk-core/.github/workflows/per-pr.yml +6 -6
- package/sdk-core/AGENTS.md +41 -30
- package/sdk-core/Cargo.toml +3 -0
- package/sdk-core/README.md +15 -9
- package/sdk-core/crates/client/Cargo.toml +4 -0
- package/sdk-core/crates/client/README.md +139 -0
- package/sdk-core/crates/client/src/async_activity_handle.rs +297 -0
- package/sdk-core/crates/client/src/callback_based.rs +7 -0
- package/sdk-core/crates/client/src/errors.rs +294 -0
- package/sdk-core/crates/client/src/{raw.rs → grpc.rs} +280 -159
- package/sdk-core/crates/client/src/lib.rs +920 -1326
- package/sdk-core/crates/client/src/metrics.rs +24 -33
- package/sdk-core/crates/client/src/options_structs.rs +457 -0
- package/sdk-core/crates/client/src/replaceable.rs +5 -4
- package/sdk-core/crates/client/src/request_extensions.rs +8 -9
- package/sdk-core/crates/client/src/retry.rs +99 -54
- package/sdk-core/crates/client/src/{worker/mod.rs → worker.rs} +1 -1
- package/sdk-core/crates/client/src/workflow_handle.rs +826 -0
- package/sdk-core/crates/common/Cargo.toml +61 -2
- package/sdk-core/crates/common/build.rs +742 -12
- package/sdk-core/crates/common/protos/api_upstream/.github/workflows/ci.yml +2 -0
- package/sdk-core/crates/common/protos/api_upstream/Makefile +2 -1
- package/sdk-core/crates/common/protos/api_upstream/buf.yaml +0 -3
- package/sdk-core/crates/common/protos/api_upstream/cmd/check-path-conflicts/main.go +137 -0
- package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +1166 -770
- package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +1243 -750
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +2 -2
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +4 -3
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +4 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +16 -1
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -6
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +88 -33
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +4 -2
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +5 -5
- package/sdk-core/crates/common/src/activity_definition.rs +20 -0
- package/sdk-core/crates/common/src/data_converters.rs +770 -0
- package/sdk-core/crates/common/src/envconfig.rs +5 -0
- package/sdk-core/crates/common/src/lib.rs +15 -211
- package/sdk-core/crates/common/src/payload_visitor.rs +648 -0
- package/sdk-core/crates/common/src/priority.rs +110 -0
- package/sdk-core/crates/common/src/protos/canned_histories.rs +3 -0
- package/sdk-core/crates/common/src/protos/history_builder.rs +45 -0
- package/sdk-core/crates/common/src/protos/history_info.rs +2 -0
- package/sdk-core/crates/common/src/protos/mod.rs +122 -27
- package/sdk-core/crates/common/src/protos/task_token.rs +3 -3
- package/sdk-core/crates/common/src/protos/utilities.rs +11 -0
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/log_export.rs +5 -7
- package/sdk-core/crates/common/src/telemetry/metrics/core.rs +125 -0
- package/sdk-core/crates/common/src/telemetry/metrics.rs +268 -223
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/otel.rs +8 -13
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_meter.rs +49 -50
- package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_server.rs +2 -3
- package/sdk-core/crates/common/src/telemetry.rs +264 -4
- package/sdk-core/crates/common/src/worker.rs +68 -603
- package/sdk-core/crates/common/src/workflow_definition.rs +60 -0
- package/sdk-core/crates/macros/Cargo.toml +5 -1
- package/sdk-core/crates/macros/src/activities_definitions.rs +585 -0
- package/sdk-core/crates/macros/src/fsm_impl.rs +507 -0
- package/sdk-core/crates/macros/src/lib.rs +138 -512
- package/sdk-core/crates/macros/src/macro_utils.rs +106 -0
- package/sdk-core/crates/macros/src/workflow_definitions.rs +1224 -0
- package/sdk-core/crates/sdk/Cargo.toml +19 -6
- package/sdk-core/crates/sdk/README.md +415 -0
- package/sdk-core/crates/sdk/src/activities.rs +417 -0
- package/sdk-core/crates/sdk/src/interceptors.rs +1 -1
- package/sdk-core/crates/sdk/src/lib.rs +757 -442
- package/sdk-core/crates/sdk/src/workflow_context/options.rs +45 -35
- package/sdk-core/crates/sdk/src/workflow_context.rs +1033 -289
- package/sdk-core/crates/sdk/src/workflow_future.rs +277 -213
- package/sdk-core/crates/sdk/src/workflows.rs +711 -0
- package/sdk-core/crates/sdk-core/Cargo.toml +57 -64
- package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +41 -35
- package/sdk-core/crates/sdk-core/machine_coverage/ActivityMachine_Coverage.puml +1 -1
- package/sdk-core/crates/sdk-core/src/abstractions.rs +6 -10
- package/sdk-core/crates/sdk-core/src/core_tests/activity_tasks.rs +6 -5
- package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +13 -15
- package/sdk-core/crates/sdk-core/src/core_tests/queries.rs +21 -25
- package/sdk-core/crates/sdk-core/src/core_tests/replay_flag.rs +7 -10
- package/sdk-core/crates/sdk-core/src/core_tests/updates.rs +14 -17
- package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +493 -26
- package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +4 -8
- package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +7 -7
- package/sdk-core/crates/sdk-core/src/histfetch.rs +20 -10
- package/sdk-core/crates/sdk-core/src/lib.rs +41 -111
- package/sdk-core/crates/sdk-core/src/pollers/mod.rs +4 -9
- package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +118 -19
- package/sdk-core/crates/sdk-core/src/protosext/mod.rs +2 -2
- package/sdk-core/crates/sdk-core/src/replay/mod.rs +14 -5
- package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +179 -196
- package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +3 -280
- package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +6 -9
- package/sdk-core/crates/sdk-core/src/test_help/unit_helpers.rs +3 -6
- package/sdk-core/crates/sdk-core/src/worker/activities/local_activities.rs +11 -14
- package/sdk-core/crates/sdk-core/src/worker/activities.rs +16 -19
- package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +9 -5
- package/sdk-core/crates/sdk-core/src/worker/client.rs +103 -81
- package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +7 -11
- package/sdk-core/crates/sdk-core/src/worker/mod.rs +1124 -229
- package/sdk-core/crates/sdk-core/src/worker/nexus.rs +145 -23
- package/sdk-core/crates/sdk-core/src/worker/slot_provider.rs +2 -2
- package/sdk-core/crates/sdk-core/src/worker/tuner/fixed_size.rs +2 -2
- package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +13 -13
- package/sdk-core/crates/sdk-core/src/worker/tuner.rs +28 -8
- package/sdk-core/crates/sdk-core/src/worker/workflow/driven_workflow.rs +9 -3
- package/sdk-core/crates/sdk-core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +21 -22
- package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +16 -3
- package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +14 -18
- package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +4 -6
- package/sdk-core/crates/sdk-core/src/worker/workflow/run_cache.rs +4 -7
- package/sdk-core/crates/sdk-core/src/worker/workflow/wft_extraction.rs +2 -4
- package/sdk-core/crates/sdk-core/src/worker/workflow/wft_poller.rs +8 -9
- package/sdk-core/crates/sdk-core/src/worker/workflow/workflow_stream.rs +1 -3
- package/sdk-core/crates/sdk-core/tests/activities_procmacro.rs +6 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/basic_pass.rs +54 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.rs +18 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.rs +14 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/multi_arg_pass.rs +48 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_input_pass.rs +14 -0
- package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_return_type_pass.rs +19 -0
- package/sdk-core/crates/sdk-core/tests/cloud_tests.rs +14 -5
- package/sdk-core/crates/sdk-core/tests/common/activity_functions.rs +55 -0
- package/sdk-core/crates/sdk-core/tests/common/mod.rs +241 -196
- package/sdk-core/crates/sdk-core/tests/common/workflows.rs +41 -28
- package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +3 -5
- package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +73 -64
- package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +298 -252
- package/sdk-core/crates/sdk-core/tests/integ_tests/async_activity_client_tests.rs +230 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/client_tests.rs +94 -57
- package/sdk-core/crates/sdk-core/tests/integ_tests/data_converter_tests.rs +381 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +16 -12
- package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +48 -40
- package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +327 -255
- package/sdk-core/crates/sdk-core/tests/integ_tests/pagination_tests.rs +50 -45
- package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +147 -126
- package/sdk-core/crates/sdk-core/tests/integ_tests/queries_tests.rs +103 -89
- package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +609 -453
- package/sdk-core/crates/sdk-core/tests/integ_tests/visibility_tests.rs +80 -62
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +360 -231
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +248 -185
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +52 -43
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_client_tests.rs +180 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +428 -315
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +82 -56
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +56 -28
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +364 -243
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/client_interactions.rs +552 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +101 -42
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +243 -147
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +98 -28
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1475 -1036
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +73 -41
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +397 -238
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +340 -188
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/queries.rs +415 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/replay.rs +96 -36
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +154 -137
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +183 -105
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +85 -38
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +142 -40
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +73 -54
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +363 -226
- package/sdk-core/crates/sdk-core/tests/main.rs +17 -15
- package/sdk-core/crates/sdk-core/tests/manual_tests.rs +207 -152
- package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +65 -34
- package/sdk-core/crates/sdk-core/tests/shared_tests/priority.rs +107 -84
- package/sdk-core/crates/sdk-core/tests/workflows_procmacro.rs +6 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.rs +26 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/basic_pass.rs +49 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/minimal_pass.rs +21 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.rs +26 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.rs +21 -0
- package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.stderr +5 -0
- package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +7 -1
- package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +14 -14
- package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +83 -74
- package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +9 -14
- package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +1 -2
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +13 -13
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +6 -6
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +3 -4
- package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +62 -75
- package/sdk-core/rustfmt.toml +2 -1
- package/src/client.rs +205 -318
- package/src/metrics.rs +22 -30
- package/src/runtime.rs +4 -5
- package/src/worker.rs +15 -18
- package/ts/native.ts +1 -1
- package/sdk-core/crates/client/src/workflow_handle/mod.rs +0 -212
- package/sdk-core/crates/common/src/errors.rs +0 -85
- package/sdk-core/crates/common/tests/worker_task_types_test.rs +0 -129
- package/sdk-core/crates/sdk/src/activity_context.rs +0 -238
- package/sdk-core/crates/sdk/src/app_data.rs +0 -37
- package/sdk-core/crates/sdk-core/tests/integ_tests/activity_functions.rs +0 -5
- 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
|
+
}
|