@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,1224 @@
|
|
|
1
|
+
use crate::macro_utils::{
|
|
2
|
+
generate_const_definition, generate_marker_struct, method_name_to_pascal_case,
|
|
3
|
+
type_name_string, type_to_snake_case,
|
|
4
|
+
};
|
|
5
|
+
use proc_macro::TokenStream;
|
|
6
|
+
use proc_macro2::TokenStream as TokenStream2;
|
|
7
|
+
use quote::{format_ident, quote};
|
|
8
|
+
use syn::{
|
|
9
|
+
Attribute, FnArg, ImplItem, ItemImpl, ReturnType, Type, TypePath,
|
|
10
|
+
parse::{Parse, ParseStream},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/// Convert an optional type to a quoted token stream, defaulting to `()` if None.
|
|
14
|
+
fn type_to_tokens(ty: Option<&Type>) -> TokenStream2 {
|
|
15
|
+
ty.map(|t| quote! { #t }).unwrap_or(quote! { () })
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// Common data extracted from handler methods (signal, query, update)
|
|
19
|
+
struct HandlerCodegenInfo {
|
|
20
|
+
struct_ident: syn::Ident,
|
|
21
|
+
prefixed_method: syn::Ident,
|
|
22
|
+
input_type_tokens: TokenStream2,
|
|
23
|
+
handler_name: TokenStream2,
|
|
24
|
+
marker_struct: TokenStream2,
|
|
25
|
+
const_def: TokenStream2,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
impl HandlerCodegenInfo {
|
|
29
|
+
fn new(
|
|
30
|
+
method: &syn::ImplItemFn,
|
|
31
|
+
attributes: &MethodAttributes,
|
|
32
|
+
input_type: Option<&Type>,
|
|
33
|
+
module_ident: &syn::Ident,
|
|
34
|
+
) -> Self {
|
|
35
|
+
let struct_name = method_name_to_pascal_case(&method.sig.ident);
|
|
36
|
+
let struct_ident = format_ident!("{}", struct_name);
|
|
37
|
+
let prefixed_method = format_ident!("__{}", method.sig.ident);
|
|
38
|
+
let input_type_tokens = type_to_tokens(input_type);
|
|
39
|
+
let handler_name = {
|
|
40
|
+
let method_ident: &syn::Ident = &method.sig.ident;
|
|
41
|
+
if let Some(ref name_expr) = attributes.name_override {
|
|
42
|
+
quote! { #name_expr }
|
|
43
|
+
} else {
|
|
44
|
+
let name = method_ident.to_string();
|
|
45
|
+
quote! { #name }
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
let marker_struct = generate_marker_struct(method);
|
|
49
|
+
let const_def = generate_const_definition(method, module_ident);
|
|
50
|
+
|
|
51
|
+
Self {
|
|
52
|
+
struct_ident,
|
|
53
|
+
prefixed_method,
|
|
54
|
+
input_type_tokens,
|
|
55
|
+
handler_name,
|
|
56
|
+
marker_struct,
|
|
57
|
+
const_def,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// Generate a method call with or without input parameter (for sync methods with &mut self)
|
|
63
|
+
fn generate_method_call(prefixed_method: &syn::Ident, has_input: bool) -> TokenStream2 {
|
|
64
|
+
if has_input {
|
|
65
|
+
quote! { self.#prefixed_method(ctx, input) }
|
|
66
|
+
} else {
|
|
67
|
+
quote! { self.#prefixed_method(ctx) }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/// Generate an async handler body. The async move block is required because the underlying
|
|
72
|
+
/// method borrows ctx, and we need to move ctx into the block to make the future 'static.
|
|
73
|
+
fn generate_async_handler_body(
|
|
74
|
+
impl_type: &Type,
|
|
75
|
+
prefixed_method: &syn::Ident,
|
|
76
|
+
has_input: bool,
|
|
77
|
+
) -> TokenStream2 {
|
|
78
|
+
let method_call = if has_input {
|
|
79
|
+
quote! { #impl_type::#prefixed_method(&mut ctx, input) }
|
|
80
|
+
} else {
|
|
81
|
+
quote! { #impl_type::#prefixed_method(&mut ctx) }
|
|
82
|
+
};
|
|
83
|
+
quote! { async move { #method_call.await }.boxed_local() }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// Generate an async update handler body that returns Result.
|
|
87
|
+
/// If is_fallible, the method already returns Result; otherwise wrap in Ok via .map().
|
|
88
|
+
fn generate_async_update_handler_body(
|
|
89
|
+
impl_type: &Type,
|
|
90
|
+
prefixed_method: &syn::Ident,
|
|
91
|
+
has_input: bool,
|
|
92
|
+
is_fallible: bool,
|
|
93
|
+
) -> TokenStream2 {
|
|
94
|
+
let method_call = if has_input {
|
|
95
|
+
quote! { #impl_type::#prefixed_method(&mut ctx, input) }
|
|
96
|
+
} else {
|
|
97
|
+
quote! { #impl_type::#prefixed_method(&mut ctx) }
|
|
98
|
+
};
|
|
99
|
+
if is_fallible {
|
|
100
|
+
quote! { async move { #method_call.await }.boxed_local() }
|
|
101
|
+
} else {
|
|
102
|
+
quote! { async move { #method_call.await }.map(Ok).boxed_local() }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// Parsed representation of a `#[workflow_methods]` impl block
|
|
107
|
+
pub(crate) struct WorkflowMethodsDefinition {
|
|
108
|
+
impl_block: ItemImpl,
|
|
109
|
+
init_method: Option<InitMethod>,
|
|
110
|
+
run_method: RunMethod,
|
|
111
|
+
signals: Vec<SignalMethod>,
|
|
112
|
+
queries: Vec<QueryMethod>,
|
|
113
|
+
updates: Vec<UpdateMethod>,
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
#[derive(Default)]
|
|
117
|
+
struct MethodAttributes {
|
|
118
|
+
name_override: Option<syn::Expr>,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
struct InitMethod {
|
|
122
|
+
method: syn::ImplItemFn,
|
|
123
|
+
input_type: Option<Type>,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
struct RunMethod {
|
|
127
|
+
method: syn::ImplItemFn,
|
|
128
|
+
attributes: MethodAttributes,
|
|
129
|
+
input_type: Option<Type>,
|
|
130
|
+
output_type: Option<Type>,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
struct SignalMethod {
|
|
134
|
+
method: syn::ImplItemFn,
|
|
135
|
+
attributes: MethodAttributes,
|
|
136
|
+
is_async: bool,
|
|
137
|
+
input_type: Option<Type>,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
struct QueryMethod {
|
|
141
|
+
method: syn::ImplItemFn,
|
|
142
|
+
attributes: MethodAttributes,
|
|
143
|
+
input_type: Option<Type>,
|
|
144
|
+
output_type: Option<Type>,
|
|
145
|
+
is_fallible: bool,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
struct UpdateMethod {
|
|
149
|
+
method: syn::ImplItemFn,
|
|
150
|
+
attributes: MethodAttributes,
|
|
151
|
+
is_async: bool,
|
|
152
|
+
input_type: Option<Type>,
|
|
153
|
+
/// The Ok type if fallible, or the direct return type if infallible
|
|
154
|
+
output_type: Option<Type>,
|
|
155
|
+
/// True if the handler returns Result<T, E>, false if it returns T directly
|
|
156
|
+
is_fallible: bool,
|
|
157
|
+
validator: Option<syn::Ident>,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
struct ValidatorMethod {
|
|
161
|
+
method: syn::ImplItemFn,
|
|
162
|
+
update_name: syn::Ident,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
impl Parse for WorkflowMethodsDefinition {
|
|
166
|
+
fn parse(input: ParseStream) -> syn::Result<Self> {
|
|
167
|
+
let impl_block: ItemImpl = input.parse()?;
|
|
168
|
+
let mut init_method = None;
|
|
169
|
+
let mut run_method = None;
|
|
170
|
+
let mut signals = Vec::new();
|
|
171
|
+
let mut queries = Vec::new();
|
|
172
|
+
let mut updates = Vec::new();
|
|
173
|
+
let mut validators = Vec::new();
|
|
174
|
+
|
|
175
|
+
for item in &impl_block.items {
|
|
176
|
+
if let ImplItem::Fn(method) = item {
|
|
177
|
+
let has_init = has_attr(&method.attrs, "init");
|
|
178
|
+
let has_run = has_attr(&method.attrs, "run");
|
|
179
|
+
let has_signal = has_attr(&method.attrs, "signal");
|
|
180
|
+
let has_query = has_attr(&method.attrs, "query");
|
|
181
|
+
let has_update = has_attr(&method.attrs, "update");
|
|
182
|
+
let has_update_validator = has_attr(&method.attrs, "update_validator");
|
|
183
|
+
|
|
184
|
+
// Count how many workflow attributes are present
|
|
185
|
+
let attr_count = [
|
|
186
|
+
has_init,
|
|
187
|
+
has_run,
|
|
188
|
+
has_signal,
|
|
189
|
+
has_query,
|
|
190
|
+
has_update,
|
|
191
|
+
has_update_validator,
|
|
192
|
+
]
|
|
193
|
+
.iter()
|
|
194
|
+
.filter(|&&b| b)
|
|
195
|
+
.count();
|
|
196
|
+
|
|
197
|
+
if attr_count > 1 {
|
|
198
|
+
return Err(syn::Error::new_spanned(
|
|
199
|
+
method,
|
|
200
|
+
"A method can only have one of #[init], #[run], #[signal], #[query], #[update], or #[update_validator]",
|
|
201
|
+
));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if has_init {
|
|
205
|
+
if init_method.is_some() {
|
|
206
|
+
return Err(syn::Error::new_spanned(
|
|
207
|
+
method,
|
|
208
|
+
"Only one #[init] method is allowed per workflow",
|
|
209
|
+
));
|
|
210
|
+
}
|
|
211
|
+
init_method = Some(parse_init_method(method)?);
|
|
212
|
+
} else if has_run {
|
|
213
|
+
if run_method.is_some() {
|
|
214
|
+
return Err(syn::Error::new_spanned(
|
|
215
|
+
method,
|
|
216
|
+
"Only one #[run] method is allowed per workflow",
|
|
217
|
+
));
|
|
218
|
+
}
|
|
219
|
+
run_method = Some(parse_run_method(method)?);
|
|
220
|
+
} else if has_signal {
|
|
221
|
+
signals.push(parse_signal_method(method)?);
|
|
222
|
+
} else if has_query {
|
|
223
|
+
queries.push(parse_query_method(method)?);
|
|
224
|
+
} else if has_update {
|
|
225
|
+
updates.push(parse_update_method(method)?);
|
|
226
|
+
} else if has_update_validator {
|
|
227
|
+
validators.push(parse_validator_method(method)?);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
let run_method = run_method.ok_or_else(|| {
|
|
233
|
+
syn::Error::new_spanned(
|
|
234
|
+
&impl_block,
|
|
235
|
+
"#[workflow_methods] requires exactly one #[run] method",
|
|
236
|
+
)
|
|
237
|
+
})?;
|
|
238
|
+
|
|
239
|
+
// Validate that all validator targets exist and link them to updates
|
|
240
|
+
for validator in &validators {
|
|
241
|
+
let target_exists = updates
|
|
242
|
+
.iter()
|
|
243
|
+
.any(|u| u.method.sig.ident == validator.update_name);
|
|
244
|
+
if !target_exists {
|
|
245
|
+
return Err(syn::Error::new_spanned(
|
|
246
|
+
&validator.method,
|
|
247
|
+
format!(
|
|
248
|
+
"No #[update] method '{}' found for validator",
|
|
249
|
+
validator.update_name
|
|
250
|
+
),
|
|
251
|
+
));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Link validators to their updates
|
|
256
|
+
for update in &mut updates {
|
|
257
|
+
if let Some(validator) = validators
|
|
258
|
+
.iter()
|
|
259
|
+
.find(|v| v.update_name == update.method.sig.ident)
|
|
260
|
+
{
|
|
261
|
+
update.validator = Some(validator.method.sig.ident.clone());
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
Ok(WorkflowMethodsDefinition {
|
|
266
|
+
impl_block,
|
|
267
|
+
init_method,
|
|
268
|
+
run_method,
|
|
269
|
+
signals,
|
|
270
|
+
queries,
|
|
271
|
+
updates,
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
fn has_attr(attrs: &[Attribute], name: &str) -> bool {
|
|
277
|
+
attrs.iter().any(|attr| attr.path().is_ident(name))
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
fn extract_method_attributes(
|
|
281
|
+
attrs: &[Attribute],
|
|
282
|
+
attr_name: &str,
|
|
283
|
+
) -> syn::Result<MethodAttributes> {
|
|
284
|
+
let mut method_attrs = MethodAttributes::default();
|
|
285
|
+
|
|
286
|
+
for attr in attrs {
|
|
287
|
+
if attr.path().is_ident(attr_name) && attr.meta.require_list().is_ok() {
|
|
288
|
+
attr.parse_nested_meta(|meta| {
|
|
289
|
+
if meta.path.is_ident("name") {
|
|
290
|
+
let value = meta.value()?;
|
|
291
|
+
let expr: syn::Expr = value.parse()?;
|
|
292
|
+
method_attrs.name_override = Some(expr);
|
|
293
|
+
Ok(())
|
|
294
|
+
} else {
|
|
295
|
+
Err(meta.error(format!("unsupported {attr_name} attribute")))
|
|
296
|
+
}
|
|
297
|
+
})?;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
Ok(method_attrs)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
fn parse_init_method(method: &syn::ImplItemFn) -> syn::Result<InitMethod> {
|
|
305
|
+
// Validate: must not be async
|
|
306
|
+
if method.sig.asyncness.is_some() {
|
|
307
|
+
return Err(syn::Error::new_spanned(
|
|
308
|
+
&method.sig,
|
|
309
|
+
"#[init] methods must not be async",
|
|
310
|
+
));
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Validate: must return Self
|
|
314
|
+
let error_msg = "#[init] methods must return Self";
|
|
315
|
+
match &method.sig.output {
|
|
316
|
+
ReturnType::Type(_, ty) => {
|
|
317
|
+
if let Type::Path(TypePath { path, .. }) = &**ty {
|
|
318
|
+
if !path.is_ident("Self") {
|
|
319
|
+
return Err(syn::Error::new_spanned(ty, error_msg));
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
return Err(syn::Error::new_spanned(ty, error_msg));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
ReturnType::Default => {
|
|
326
|
+
return Err(syn::Error::new_spanned(&method.sig, error_msg));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
let input_type = extract_input_type(&method.sig)?;
|
|
330
|
+
|
|
331
|
+
Ok(InitMethod {
|
|
332
|
+
method: method.clone(),
|
|
333
|
+
input_type,
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
fn parse_run_method(method: &syn::ImplItemFn) -> syn::Result<RunMethod> {
|
|
338
|
+
let attributes = extract_method_attributes(&method.attrs, "run")?;
|
|
339
|
+
|
|
340
|
+
if method.sig.asyncness.is_none() {
|
|
341
|
+
return Err(syn::Error::new_spanned(
|
|
342
|
+
&method.sig,
|
|
343
|
+
"#[run] methods must be async",
|
|
344
|
+
));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Async run methods must not have self to prevent unsound references across awaits
|
|
348
|
+
validate_no_self_receiver_for_async(method, "run")?;
|
|
349
|
+
|
|
350
|
+
let input_type = extract_input_type(&method.sig)?;
|
|
351
|
+
let output_type = extract_workflow_result_type(&method.sig)?;
|
|
352
|
+
|
|
353
|
+
Ok(RunMethod {
|
|
354
|
+
method: method.clone(),
|
|
355
|
+
attributes,
|
|
356
|
+
input_type,
|
|
357
|
+
output_type,
|
|
358
|
+
})
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
fn parse_signal_method(method: &syn::ImplItemFn) -> syn::Result<SignalMethod> {
|
|
362
|
+
let attributes = extract_method_attributes(&method.attrs, "signal")?;
|
|
363
|
+
let is_async = method.sig.asyncness.is_some();
|
|
364
|
+
|
|
365
|
+
// Async signals must not have self, sync signals use &mut self
|
|
366
|
+
if is_async {
|
|
367
|
+
validate_no_self_receiver_for_async(method, "signal")?;
|
|
368
|
+
} else {
|
|
369
|
+
validate_mut_self_receiver(method)?;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
let input_type = extract_input_type(&method.sig)?;
|
|
373
|
+
|
|
374
|
+
Ok(SignalMethod {
|
|
375
|
+
method: method.clone(),
|
|
376
|
+
attributes,
|
|
377
|
+
is_async,
|
|
378
|
+
input_type,
|
|
379
|
+
})
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
fn parse_query_method(method: &syn::ImplItemFn) -> syn::Result<QueryMethod> {
|
|
383
|
+
let attributes = extract_method_attributes(&method.attrs, "query")?;
|
|
384
|
+
|
|
385
|
+
if method.sig.asyncness.is_some() {
|
|
386
|
+
return Err(syn::Error::new_spanned(
|
|
387
|
+
&method.sig,
|
|
388
|
+
"#[query] methods must not be async (queries are synchronous)",
|
|
389
|
+
));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
validate_immut_self_receiver(method)?;
|
|
393
|
+
|
|
394
|
+
let input_type = extract_input_type(&method.sig)?;
|
|
395
|
+
let (output_type, is_fallible) = extract_maybe_result_output_type(&method.sig);
|
|
396
|
+
|
|
397
|
+
Ok(QueryMethod {
|
|
398
|
+
method: method.clone(),
|
|
399
|
+
attributes,
|
|
400
|
+
input_type,
|
|
401
|
+
output_type,
|
|
402
|
+
is_fallible,
|
|
403
|
+
})
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
fn parse_update_method(method: &syn::ImplItemFn) -> syn::Result<UpdateMethod> {
|
|
407
|
+
let attributes = extract_method_attributes(&method.attrs, "update")?;
|
|
408
|
+
let is_async = method.sig.asyncness.is_some();
|
|
409
|
+
|
|
410
|
+
// Async updates must not have self, sync updates use &mut self
|
|
411
|
+
if is_async {
|
|
412
|
+
validate_no_self_receiver_for_async(method, "update")?;
|
|
413
|
+
} else {
|
|
414
|
+
validate_mut_self_receiver(method)?;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
let input_type = extract_input_type(&method.sig)?;
|
|
418
|
+
let (output_type, is_fallible) = extract_maybe_result_output_type(&method.sig);
|
|
419
|
+
|
|
420
|
+
Ok(UpdateMethod {
|
|
421
|
+
method: method.clone(),
|
|
422
|
+
attributes,
|
|
423
|
+
is_async,
|
|
424
|
+
input_type,
|
|
425
|
+
output_type,
|
|
426
|
+
is_fallible,
|
|
427
|
+
validator: None,
|
|
428
|
+
})
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
fn parse_validator_method(method: &syn::ImplItemFn) -> syn::Result<ValidatorMethod> {
|
|
432
|
+
if method.sig.asyncness.is_some() {
|
|
433
|
+
return Err(syn::Error::new_spanned(
|
|
434
|
+
&method.sig,
|
|
435
|
+
"#[update_validator] methods must not be async",
|
|
436
|
+
));
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
validate_immut_self_receiver(method)?;
|
|
440
|
+
|
|
441
|
+
let update_name = extract_validator_target(&method.attrs)?;
|
|
442
|
+
|
|
443
|
+
Ok(ValidatorMethod {
|
|
444
|
+
method: method.clone(),
|
|
445
|
+
update_name,
|
|
446
|
+
})
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
fn extract_validator_target(attrs: &[Attribute]) -> syn::Result<syn::Ident> {
|
|
450
|
+
for attr in attrs {
|
|
451
|
+
if attr.path().is_ident("update_validator") {
|
|
452
|
+
return attr.parse_args();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
unreachable!("extract_validator_target called without update_validator attribute")
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
fn validate_mut_self_receiver(method: &syn::ImplItemFn) -> syn::Result<()> {
|
|
459
|
+
match method.sig.inputs.first() {
|
|
460
|
+
Some(FnArg::Receiver(receiver)) => {
|
|
461
|
+
if receiver.reference.is_none() || receiver.mutability.is_none() {
|
|
462
|
+
return Err(syn::Error::new_spanned(
|
|
463
|
+
receiver,
|
|
464
|
+
"This method must use `&mut self` as the receiver",
|
|
465
|
+
));
|
|
466
|
+
}
|
|
467
|
+
Ok(())
|
|
468
|
+
}
|
|
469
|
+
_ => Err(syn::Error::new_spanned(
|
|
470
|
+
&method.sig,
|
|
471
|
+
"This method must have `&mut self` as the first parameter",
|
|
472
|
+
)),
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
fn validate_immut_self_receiver(method: &syn::ImplItemFn) -> syn::Result<()> {
|
|
477
|
+
match method.sig.inputs.first() {
|
|
478
|
+
Some(FnArg::Receiver(receiver)) => {
|
|
479
|
+
if receiver.reference.is_none() {
|
|
480
|
+
return Err(syn::Error::new_spanned(
|
|
481
|
+
receiver,
|
|
482
|
+
"#[query] methods must use `&self` as the receiver (not `self`)",
|
|
483
|
+
));
|
|
484
|
+
}
|
|
485
|
+
if receiver.mutability.is_some() {
|
|
486
|
+
return Err(syn::Error::new_spanned(
|
|
487
|
+
receiver,
|
|
488
|
+
"#[query] methods must use `&self` as the receiver (not `&mut self`) - queries are read-only",
|
|
489
|
+
));
|
|
490
|
+
}
|
|
491
|
+
Ok(())
|
|
492
|
+
}
|
|
493
|
+
_ => Err(syn::Error::new_spanned(
|
|
494
|
+
&method.sig,
|
|
495
|
+
"#[query] methods must have `&self` as the first parameter",
|
|
496
|
+
)),
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
fn validate_no_self_receiver_for_async(
|
|
501
|
+
method: &syn::ImplItemFn,
|
|
502
|
+
attr_name: &str,
|
|
503
|
+
) -> syn::Result<()> {
|
|
504
|
+
// Async workflow methods must NOT have a self receiver to prevent unsound references
|
|
505
|
+
// across await points (signals/updates can mutate state during awaits).
|
|
506
|
+
if let Some(FnArg::Receiver(receiver)) = method.sig.inputs.first() {
|
|
507
|
+
return Err(syn::Error::new_spanned(
|
|
508
|
+
receiver,
|
|
509
|
+
format!(
|
|
510
|
+
"Async #[{attr_name}] methods must not have a self parameter. \
|
|
511
|
+
Use WorkflowContext to access state safely."
|
|
512
|
+
),
|
|
513
|
+
));
|
|
514
|
+
}
|
|
515
|
+
Ok(())
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
fn extract_input_type(sig: &syn::Signature) -> syn::Result<Option<Type>> {
|
|
519
|
+
// Skip self (if present), then find parameter after context
|
|
520
|
+
let mut found_ctx = false;
|
|
521
|
+
for arg in &sig.inputs {
|
|
522
|
+
match arg {
|
|
523
|
+
FnArg::Receiver(_) => continue,
|
|
524
|
+
FnArg::Typed(pat_type) => {
|
|
525
|
+
if found_ctx {
|
|
526
|
+
return Ok(Some((*pat_type.ty).clone()));
|
|
527
|
+
}
|
|
528
|
+
if is_wf_context_type(&pat_type.ty) {
|
|
529
|
+
found_ctx = true;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
Ok(None)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
fn is_wf_context_type(ty: &Type) -> bool {
|
|
538
|
+
// Check for WorkflowContext, SyncWorkflowContext, or WorkflowContextView
|
|
539
|
+
let inner_type = match ty {
|
|
540
|
+
Type::Reference(r) => &*r.elem,
|
|
541
|
+
other => other,
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
if let Type::Path(TypePath { path, .. }) = inner_type
|
|
545
|
+
&& let Some(segment) = path.segments.last()
|
|
546
|
+
{
|
|
547
|
+
return segment.ident == "WorkflowContext"
|
|
548
|
+
|| segment.ident == "SyncWorkflowContext"
|
|
549
|
+
|| segment.ident == "WorkflowContextView";
|
|
550
|
+
}
|
|
551
|
+
false
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
fn extract_workflow_result_type(sig: &syn::Signature) -> syn::Result<Option<Type>> {
|
|
555
|
+
match &sig.output {
|
|
556
|
+
ReturnType::Type(_, ty) => {
|
|
557
|
+
if let Type::Path(TypePath { path, .. }) = &**ty
|
|
558
|
+
&& let Some(segment) = path.segments.last()
|
|
559
|
+
&& segment.ident == "WorkflowResult"
|
|
560
|
+
&& let syn::PathArguments::AngleBracketed(args) = &segment.arguments
|
|
561
|
+
&& let Some(syn::GenericArgument::Type(output_ty)) = args.args.first()
|
|
562
|
+
{
|
|
563
|
+
return Ok(Some(output_ty.clone()));
|
|
564
|
+
}
|
|
565
|
+
Ok(None)
|
|
566
|
+
}
|
|
567
|
+
ReturnType::Default => Ok(None),
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/// Check if a return type looks like a Result and extract the Ok type if so.
|
|
572
|
+
/// Returns (ok_type, is_result) where:
|
|
573
|
+
/// - If the type looks like `Result<T, E>`, returns (T, true)
|
|
574
|
+
/// - Otherwise returns (original_type, false)
|
|
575
|
+
fn extract_maybe_result_output_type(sig: &syn::Signature) -> (Option<Type>, bool) {
|
|
576
|
+
match &sig.output {
|
|
577
|
+
ReturnType::Default => (None, false),
|
|
578
|
+
ReturnType::Type(_, ty) => {
|
|
579
|
+
if let Type::Path(TypePath { path, .. }) = &**ty
|
|
580
|
+
&& let Some(segment) = path.segments.last()
|
|
581
|
+
&& segment.ident == "Result"
|
|
582
|
+
&& let syn::PathArguments::AngleBracketed(args) = &segment.arguments
|
|
583
|
+
&& let Some(syn::GenericArgument::Type(ok_ty)) = args.args.first()
|
|
584
|
+
{
|
|
585
|
+
(Some(ok_ty.clone()), true)
|
|
586
|
+
} else {
|
|
587
|
+
(Some((**ty).clone()), false)
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
impl WorkflowMethodsDefinition {
|
|
594
|
+
fn run_struct_ident(&self) -> syn::Ident {
|
|
595
|
+
let struct_name = method_name_to_pascal_case(&self.run_method.method.sig.ident);
|
|
596
|
+
format_ident!("{}", struct_name)
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
pub(crate) fn codegen_with_options(&self, factory_only: bool) -> TokenStream {
|
|
600
|
+
let impl_type = &self.impl_block.self_ty;
|
|
601
|
+
let impl_type_name = type_name_string(impl_type);
|
|
602
|
+
let module_name = type_to_snake_case(impl_type);
|
|
603
|
+
let module_ident = format_ident!("{}", module_name);
|
|
604
|
+
|
|
605
|
+
// Generate the cleaned impl block with:
|
|
606
|
+
// - workflow attributes stripped
|
|
607
|
+
// - Annotated methods renamed with __ prefix
|
|
608
|
+
let mut cleaned_impl = self.impl_block.clone();
|
|
609
|
+
for item in &mut cleaned_impl.items {
|
|
610
|
+
if let ImplItem::Fn(method) = item {
|
|
611
|
+
let is_workflow_method = has_attr(&method.attrs, "init")
|
|
612
|
+
|| has_attr(&method.attrs, "run")
|
|
613
|
+
|| has_attr(&method.attrs, "signal")
|
|
614
|
+
|| has_attr(&method.attrs, "query")
|
|
615
|
+
|| has_attr(&method.attrs, "update")
|
|
616
|
+
|| has_attr(&method.attrs, "update_validator");
|
|
617
|
+
|
|
618
|
+
// Strip workflow-related attributes
|
|
619
|
+
method.attrs.retain(|attr| {
|
|
620
|
+
!attr.path().is_ident("init")
|
|
621
|
+
&& !attr.path().is_ident("run")
|
|
622
|
+
&& !attr.path().is_ident("signal")
|
|
623
|
+
&& !attr.path().is_ident("query")
|
|
624
|
+
&& !attr.path().is_ident("update")
|
|
625
|
+
&& !attr.path().is_ident("update_validator")
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
// Rename workflow methods with __ prefix
|
|
629
|
+
if is_workflow_method {
|
|
630
|
+
let new_name = format_ident!("__{}", method.sig.ident);
|
|
631
|
+
method.sig.ident = new_name;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
let mut marker_structs = Vec::new();
|
|
637
|
+
let mut const_definitions = Vec::new();
|
|
638
|
+
let mut trait_impls = Vec::new();
|
|
639
|
+
|
|
640
|
+
// Run method marker struct and impls
|
|
641
|
+
let run_method = &self.run_method;
|
|
642
|
+
let struct_ident = self.run_struct_ident();
|
|
643
|
+
|
|
644
|
+
let input_type = type_to_tokens(
|
|
645
|
+
run_method.input_type.as_ref().or(self
|
|
646
|
+
.init_method
|
|
647
|
+
.as_ref()
|
|
648
|
+
.and_then(|m| m.input_type.as_ref())),
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
let output_type = type_to_tokens(run_method.output_type.as_ref());
|
|
652
|
+
|
|
653
|
+
let workflow_name = if let Some(ref name_expr) = run_method.attributes.name_override {
|
|
654
|
+
quote! { #name_expr }
|
|
655
|
+
} else {
|
|
656
|
+
quote! { #impl_type_name }
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
marker_structs.push(generate_marker_struct(&run_method.method));
|
|
660
|
+
const_definitions.push(generate_const_definition(&run_method.method, &module_ident));
|
|
661
|
+
|
|
662
|
+
trait_impls.push(quote! {
|
|
663
|
+
impl ::temporalio_common::WorkflowDefinition for #module_ident::#struct_ident {
|
|
664
|
+
type Input = #input_type;
|
|
665
|
+
type Output = #output_type;
|
|
666
|
+
|
|
667
|
+
fn name(&self) -> &str {
|
|
668
|
+
#workflow_name
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
impl ::temporalio_common::WorkflowDefinition for #impl_type {
|
|
673
|
+
type Input = #input_type;
|
|
674
|
+
type Output = #output_type;
|
|
675
|
+
|
|
676
|
+
fn name(&self) -> &str {
|
|
677
|
+
#workflow_name
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
for signal in &self.signals {
|
|
683
|
+
let (structs, consts, impls) =
|
|
684
|
+
self.generate_signal_code(signal, impl_type, &module_ident);
|
|
685
|
+
marker_structs.push(structs);
|
|
686
|
+
const_definitions.push(consts);
|
|
687
|
+
trait_impls.push(impls);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
for query in &self.queries {
|
|
691
|
+
let (structs, consts, impls) =
|
|
692
|
+
self.generate_query_code(query, impl_type, &module_ident);
|
|
693
|
+
marker_structs.push(structs);
|
|
694
|
+
const_definitions.push(consts);
|
|
695
|
+
trait_impls.push(impls);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
for update in &self.updates {
|
|
699
|
+
let (structs, consts, impls) =
|
|
700
|
+
self.generate_update_code(update, impl_type, &module_ident);
|
|
701
|
+
marker_structs.push(structs);
|
|
702
|
+
const_definitions.push(consts);
|
|
703
|
+
trait_impls.push(impls);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
let workflow_impl =
|
|
707
|
+
self.generate_workflow_implementation(impl_type, &module_ident, factory_only);
|
|
708
|
+
let implementer_impl = self.generate_workflow_implementer(impl_type);
|
|
709
|
+
|
|
710
|
+
let const_impl = quote! {
|
|
711
|
+
impl #impl_type {
|
|
712
|
+
/// Returns the workflow type name
|
|
713
|
+
pub const fn name() -> &'static str {
|
|
714
|
+
#workflow_name
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
#(#const_definitions)*
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
let output = quote! {
|
|
722
|
+
#cleaned_impl
|
|
723
|
+
|
|
724
|
+
#const_impl
|
|
725
|
+
|
|
726
|
+
mod #module_ident {
|
|
727
|
+
#(#marker_structs)*
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
#(#trait_impls)*
|
|
731
|
+
|
|
732
|
+
#workflow_impl
|
|
733
|
+
|
|
734
|
+
#implementer_impl
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
output.into()
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
fn generate_signal_code(
|
|
741
|
+
&self,
|
|
742
|
+
signal: &SignalMethod,
|
|
743
|
+
impl_type: &Type,
|
|
744
|
+
module_ident: &syn::Ident,
|
|
745
|
+
) -> (TokenStream2, TokenStream2, TokenStream2) {
|
|
746
|
+
let info = HandlerCodegenInfo::new(
|
|
747
|
+
&signal.method,
|
|
748
|
+
&signal.attributes,
|
|
749
|
+
signal.input_type.as_ref(),
|
|
750
|
+
module_ident,
|
|
751
|
+
);
|
|
752
|
+
let struct_ident = &info.struct_ident;
|
|
753
|
+
let input_type = &info.input_type_tokens;
|
|
754
|
+
let signal_name = &info.handler_name;
|
|
755
|
+
|
|
756
|
+
let run_struct_ident = self.run_struct_ident();
|
|
757
|
+
let definition_impl = quote! {
|
|
758
|
+
impl ::temporalio_common::SignalDefinition for #module_ident::#struct_ident {
|
|
759
|
+
type Workflow = #module_ident::#run_struct_ident;
|
|
760
|
+
type Input = #input_type;
|
|
761
|
+
|
|
762
|
+
fn name(&self) -> &str {
|
|
763
|
+
#signal_name
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
let has_input = signal.input_type.is_some();
|
|
769
|
+
let executable_impl = if signal.is_async {
|
|
770
|
+
let handle_body =
|
|
771
|
+
generate_async_handler_body(impl_type, &info.prefixed_method, has_input);
|
|
772
|
+
quote! {
|
|
773
|
+
impl ::temporalio_sdk::workflows::ExecutableAsyncSignal<#module_ident::#struct_ident> for #impl_type {
|
|
774
|
+
fn handle(
|
|
775
|
+
mut ctx: ::temporalio_sdk::WorkflowContext<Self>,
|
|
776
|
+
input: <#module_ident::#struct_ident as ::temporalio_common::SignalDefinition>::Input,
|
|
777
|
+
) -> ::futures_util::future::LocalBoxFuture<'static, ()> {
|
|
778
|
+
use ::futures_util::FutureExt;
|
|
779
|
+
#handle_body
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
} else {
|
|
784
|
+
let method_call = generate_method_call(&info.prefixed_method, has_input);
|
|
785
|
+
quote! {
|
|
786
|
+
impl ::temporalio_sdk::workflows::ExecutableSyncSignal<#module_ident::#struct_ident> for #impl_type {
|
|
787
|
+
fn handle(
|
|
788
|
+
&mut self,
|
|
789
|
+
ctx: &mut ::temporalio_sdk::SyncWorkflowContext<Self>,
|
|
790
|
+
input: <#module_ident::#struct_ident as ::temporalio_common::SignalDefinition>::Input,
|
|
791
|
+
) {
|
|
792
|
+
#method_call
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
let trait_impl = quote! {
|
|
799
|
+
#definition_impl
|
|
800
|
+
#executable_impl
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
(info.marker_struct, info.const_def, trait_impl)
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
fn generate_query_code(
|
|
807
|
+
&self,
|
|
808
|
+
query: &QueryMethod,
|
|
809
|
+
impl_type: &Type,
|
|
810
|
+
module_ident: &syn::Ident,
|
|
811
|
+
) -> (TokenStream2, TokenStream2, TokenStream2) {
|
|
812
|
+
let info = HandlerCodegenInfo::new(
|
|
813
|
+
&query.method,
|
|
814
|
+
&query.attributes,
|
|
815
|
+
query.input_type.as_ref(),
|
|
816
|
+
module_ident,
|
|
817
|
+
);
|
|
818
|
+
let struct_ident = &info.struct_ident;
|
|
819
|
+
let prefixed_method = &info.prefixed_method;
|
|
820
|
+
let input_type = &info.input_type_tokens;
|
|
821
|
+
let output_type = type_to_tokens(query.output_type.as_ref());
|
|
822
|
+
let query_name = &info.handler_name;
|
|
823
|
+
|
|
824
|
+
let method_call = generate_method_call(prefixed_method, query.input_type.is_some());
|
|
825
|
+
|
|
826
|
+
let body = if query.is_fallible {
|
|
827
|
+
quote! { #method_call }
|
|
828
|
+
} else {
|
|
829
|
+
quote! { Ok(#method_call) }
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
let run_struct_ident = self.run_struct_ident();
|
|
833
|
+
let trait_impl = quote! {
|
|
834
|
+
impl ::temporalio_common::QueryDefinition for #module_ident::#struct_ident {
|
|
835
|
+
type Workflow = #module_ident::#run_struct_ident;
|
|
836
|
+
type Input = #input_type;
|
|
837
|
+
type Output = #output_type;
|
|
838
|
+
|
|
839
|
+
fn name(&self) -> &str {
|
|
840
|
+
#query_name
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
impl ::temporalio_sdk::workflows::ExecutableQuery<#module_ident::#struct_ident> for #impl_type {
|
|
845
|
+
fn handle(
|
|
846
|
+
&self,
|
|
847
|
+
ctx: &::temporalio_sdk::WorkflowContextView,
|
|
848
|
+
input: <#module_ident::#struct_ident as ::temporalio_common::QueryDefinition>::Input,
|
|
849
|
+
) -> Result<<#module_ident::#struct_ident as ::temporalio_common::QueryDefinition>::Output, Box<dyn ::std::error::Error + Send + Sync>> {
|
|
850
|
+
#body
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
(info.marker_struct, info.const_def, trait_impl)
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
fn generate_update_code(
|
|
859
|
+
&self,
|
|
860
|
+
update: &UpdateMethod,
|
|
861
|
+
impl_type: &Type,
|
|
862
|
+
module_ident: &syn::Ident,
|
|
863
|
+
) -> (TokenStream2, TokenStream2, TokenStream2) {
|
|
864
|
+
let info = HandlerCodegenInfo::new(
|
|
865
|
+
&update.method,
|
|
866
|
+
&update.attributes,
|
|
867
|
+
update.input_type.as_ref(),
|
|
868
|
+
module_ident,
|
|
869
|
+
);
|
|
870
|
+
let struct_ident = &info.struct_ident;
|
|
871
|
+
let input_type = &info.input_type_tokens;
|
|
872
|
+
let output_type = type_to_tokens(update.output_type.as_ref());
|
|
873
|
+
let update_name = &info.handler_name;
|
|
874
|
+
|
|
875
|
+
let run_struct_ident = self.run_struct_ident();
|
|
876
|
+
let definition_impl = quote! {
|
|
877
|
+
impl ::temporalio_common::UpdateDefinition for #module_ident::#struct_ident {
|
|
878
|
+
type Workflow = #module_ident::#run_struct_ident;
|
|
879
|
+
type Input = #input_type;
|
|
880
|
+
type Output = #output_type;
|
|
881
|
+
|
|
882
|
+
fn name(&self) -> &str {
|
|
883
|
+
#update_name
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
let has_input = update.input_type.is_some();
|
|
889
|
+
let validate_impl = if let Some(ref validator_name) = update.validator {
|
|
890
|
+
let prefixed_validator = format_ident!("__{}", validator_name);
|
|
891
|
+
quote! {
|
|
892
|
+
fn validate(&self, ctx: &::temporalio_sdk::WorkflowContextView, input: &<#module_ident::#struct_ident as ::temporalio_common::UpdateDefinition>::Input) -> Result<(), Box<dyn ::std::error::Error + Send + Sync>> {
|
|
893
|
+
self.#prefixed_validator(ctx, input)
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
} else {
|
|
897
|
+
quote! {}
|
|
898
|
+
};
|
|
899
|
+
|
|
900
|
+
let executable_impl = if update.is_async {
|
|
901
|
+
let handle_body = generate_async_update_handler_body(
|
|
902
|
+
impl_type,
|
|
903
|
+
&info.prefixed_method,
|
|
904
|
+
has_input,
|
|
905
|
+
update.is_fallible,
|
|
906
|
+
);
|
|
907
|
+
quote! {
|
|
908
|
+
impl ::temporalio_sdk::workflows::ExecutableAsyncUpdate<#module_ident::#struct_ident> for #impl_type {
|
|
909
|
+
fn handle(
|
|
910
|
+
mut ctx: ::temporalio_sdk::WorkflowContext<Self>,
|
|
911
|
+
input: <#module_ident::#struct_ident as ::temporalio_common::UpdateDefinition>::Input,
|
|
912
|
+
) -> ::futures_util::future::LocalBoxFuture<'static, Result<<#module_ident::#struct_ident as ::temporalio_common::UpdateDefinition>::Output, Box<dyn ::std::error::Error + Send + Sync>>> {
|
|
913
|
+
use ::futures_util::FutureExt;
|
|
914
|
+
#handle_body
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
#validate_impl
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
} else {
|
|
921
|
+
let method_call = generate_method_call(&info.prefixed_method, has_input);
|
|
922
|
+
let body = if update.is_fallible {
|
|
923
|
+
quote! { #method_call }
|
|
924
|
+
} else {
|
|
925
|
+
quote! { Ok(#method_call) }
|
|
926
|
+
};
|
|
927
|
+
quote! {
|
|
928
|
+
impl ::temporalio_sdk::workflows::ExecutableSyncUpdate<#module_ident::#struct_ident> for #impl_type {
|
|
929
|
+
fn handle(
|
|
930
|
+
&mut self,
|
|
931
|
+
ctx: &mut ::temporalio_sdk::SyncWorkflowContext<Self>,
|
|
932
|
+
input: <#module_ident::#struct_ident as ::temporalio_common::UpdateDefinition>::Input,
|
|
933
|
+
) -> Result<<#module_ident::#struct_ident as ::temporalio_common::UpdateDefinition>::Output, Box<dyn ::std::error::Error + Send + Sync>> {
|
|
934
|
+
#body
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
#validate_impl
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
let trait_impl = quote! {
|
|
943
|
+
#definition_impl
|
|
944
|
+
#executable_impl
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
(info.marker_struct, info.const_def, trait_impl)
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
fn generate_workflow_implementation(
|
|
951
|
+
&self,
|
|
952
|
+
impl_type: &Type,
|
|
953
|
+
module_ident: &syn::Ident,
|
|
954
|
+
factory_only: bool,
|
|
955
|
+
) -> TokenStream2 {
|
|
956
|
+
let run_method = &self.run_method;
|
|
957
|
+
|
|
958
|
+
let run_struct_name = method_name_to_pascal_case(&run_method.method.sig.ident);
|
|
959
|
+
let run_struct_ident = format_ident!("{}", run_struct_name);
|
|
960
|
+
let prefixed_run = format_ident!("__{}", run_method.method.sig.ident);
|
|
961
|
+
|
|
962
|
+
// Determine if init exists and takes input, or if run takes input
|
|
963
|
+
let has_init = self.init_method.is_some();
|
|
964
|
+
let init_has_input = self
|
|
965
|
+
.init_method
|
|
966
|
+
.as_ref()
|
|
967
|
+
.map(|m| m.input_type.is_some())
|
|
968
|
+
.unwrap_or(false);
|
|
969
|
+
let run_has_input = run_method.input_type.is_some();
|
|
970
|
+
|
|
971
|
+
let init_body = if let Some(ref init_method) = self.init_method {
|
|
972
|
+
let prefixed_init = format_ident!("__{}", init_method.method.sig.ident);
|
|
973
|
+
if init_has_input {
|
|
974
|
+
quote! {
|
|
975
|
+
#impl_type::#prefixed_init(&ctx, input.expect("init takes input"))
|
|
976
|
+
}
|
|
977
|
+
} else {
|
|
978
|
+
quote! {
|
|
979
|
+
#impl_type::#prefixed_init(&ctx)
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
} else if factory_only {
|
|
983
|
+
// For factory-only workflows, init() should never be called
|
|
984
|
+
quote! {
|
|
985
|
+
panic!(
|
|
986
|
+
"This workflow must be registered with register_workflow_with_factory"
|
|
987
|
+
)
|
|
988
|
+
}
|
|
989
|
+
} else {
|
|
990
|
+
quote! {
|
|
991
|
+
Self::default()
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
|
|
995
|
+
let run_call = if run_has_input {
|
|
996
|
+
quote! { Self::#prefixed_run(&mut ctx, input.expect("run takes input")).await }
|
|
997
|
+
} else {
|
|
998
|
+
quote! { Self::#prefixed_run(&mut ctx).await }
|
|
999
|
+
};
|
|
1000
|
+
|
|
1001
|
+
let run_impl_body = quote! {
|
|
1002
|
+
use ::futures_util::FutureExt;
|
|
1003
|
+
async move {
|
|
1004
|
+
let result = #run_call;
|
|
1005
|
+
match result {
|
|
1006
|
+
Ok(value) => ::temporalio_sdk::workflows::serialize_result(value, &ctx.payload_converter())
|
|
1007
|
+
.map_err(|e| ::temporalio_sdk::WorkflowTermination::failed(e)),
|
|
1008
|
+
Err(e) => Err(e),
|
|
1009
|
+
}
|
|
1010
|
+
}.boxed_local()
|
|
1011
|
+
};
|
|
1012
|
+
|
|
1013
|
+
// Generate signal dispatch match arms
|
|
1014
|
+
let dispatch_signal_arms: Vec<TokenStream2> = self
|
|
1015
|
+
.signals
|
|
1016
|
+
.iter()
|
|
1017
|
+
.map(|s| {
|
|
1018
|
+
let info = HandlerCodegenInfo::new(
|
|
1019
|
+
&s.method,
|
|
1020
|
+
&s.attributes,
|
|
1021
|
+
s.input_type.as_ref(),
|
|
1022
|
+
module_ident,
|
|
1023
|
+
);
|
|
1024
|
+
let struct_ident = &info.struct_ident;
|
|
1025
|
+
let handler_name = &info.handler_name;
|
|
1026
|
+
|
|
1027
|
+
if s.is_async {
|
|
1028
|
+
quote! {
|
|
1029
|
+
#handler_name => Some(<Self as ::temporalio_sdk::workflows::ExecutableAsyncSignal<#module_ident::#struct_ident>>::dispatch(ctx.clone(), payloads, converter)),
|
|
1030
|
+
}
|
|
1031
|
+
} else {
|
|
1032
|
+
quote! {
|
|
1033
|
+
#handler_name => Some(<Self as ::temporalio_sdk::workflows::ExecutableSyncSignal<#module_ident::#struct_ident>>::dispatch(ctx, payloads, converter)),
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
})
|
|
1037
|
+
.collect();
|
|
1038
|
+
|
|
1039
|
+
// Generate dispatch_signal only if there are signals
|
|
1040
|
+
let dispatch_signal_impl = if self.signals.is_empty() {
|
|
1041
|
+
quote! {}
|
|
1042
|
+
} else {
|
|
1043
|
+
quote! {
|
|
1044
|
+
fn dispatch_signal(
|
|
1045
|
+
ctx: ::temporalio_sdk::WorkflowContext<Self>,
|
|
1046
|
+
name: &str,
|
|
1047
|
+
payloads: ::temporalio_common::protos::temporal::api::common::v1::Payloads,
|
|
1048
|
+
converter: &::temporalio_common::data_converters::PayloadConverter,
|
|
1049
|
+
) -> Option<::futures_util::future::LocalBoxFuture<'static, Result<(), ::temporalio_sdk::workflows::WorkflowError>>> {
|
|
1050
|
+
match name {
|
|
1051
|
+
#(#dispatch_signal_arms)*
|
|
1052
|
+
_ => None,
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
|
|
1058
|
+
// Generate update dispatch match arms
|
|
1059
|
+
let dispatch_update_arms: Vec<TokenStream2> = self
|
|
1060
|
+
.updates
|
|
1061
|
+
.iter()
|
|
1062
|
+
.map(|u| {
|
|
1063
|
+
let info = HandlerCodegenInfo::new(
|
|
1064
|
+
&u.method,
|
|
1065
|
+
&u.attributes,
|
|
1066
|
+
u.input_type.as_ref(),
|
|
1067
|
+
module_ident,
|
|
1068
|
+
);
|
|
1069
|
+
let struct_ident = &info.struct_ident;
|
|
1070
|
+
let handler_name = &info.handler_name;
|
|
1071
|
+
|
|
1072
|
+
if u.is_async {
|
|
1073
|
+
quote! {
|
|
1074
|
+
#handler_name => Some(<Self as ::temporalio_sdk::workflows::ExecutableAsyncUpdate<#module_ident::#struct_ident>>::dispatch(ctx.clone(), payloads, converter)),
|
|
1075
|
+
}
|
|
1076
|
+
} else {
|
|
1077
|
+
quote! {
|
|
1078
|
+
#handler_name => Some(<Self as ::temporalio_sdk::workflows::ExecutableSyncUpdate<#module_ident::#struct_ident>>::dispatch(ctx, payloads, converter)),
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
})
|
|
1082
|
+
.collect();
|
|
1083
|
+
|
|
1084
|
+
// Generate validate_update match arms
|
|
1085
|
+
let validate_update_arms: Vec<TokenStream2> = self
|
|
1086
|
+
.updates
|
|
1087
|
+
.iter()
|
|
1088
|
+
.map(|u| {
|
|
1089
|
+
let info = HandlerCodegenInfo::new(
|
|
1090
|
+
&u.method,
|
|
1091
|
+
&u.attributes,
|
|
1092
|
+
u.input_type.as_ref(),
|
|
1093
|
+
module_ident,
|
|
1094
|
+
);
|
|
1095
|
+
let struct_ident = &info.struct_ident;
|
|
1096
|
+
let handler_name = &info.handler_name;
|
|
1097
|
+
|
|
1098
|
+
let validate_trait = if u.is_async {
|
|
1099
|
+
quote! { ::temporalio_sdk::workflows::ExecutableAsyncUpdate<#module_ident::#struct_ident> }
|
|
1100
|
+
} else {
|
|
1101
|
+
quote! { ::temporalio_sdk::workflows::ExecutableSyncUpdate<#module_ident::#struct_ident> }
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
quote! {
|
|
1105
|
+
#handler_name => Some(<Self as #validate_trait>::dispatch_validate(self, &ctx, payloads, converter)),
|
|
1106
|
+
}
|
|
1107
|
+
})
|
|
1108
|
+
.collect();
|
|
1109
|
+
|
|
1110
|
+
// Generate dispatch_update and validate_update only if there are updates
|
|
1111
|
+
let dispatch_update_impl = if self.updates.is_empty() {
|
|
1112
|
+
quote! {}
|
|
1113
|
+
} else {
|
|
1114
|
+
quote! {
|
|
1115
|
+
fn dispatch_update(
|
|
1116
|
+
ctx: ::temporalio_sdk::WorkflowContext<Self>,
|
|
1117
|
+
name: &str,
|
|
1118
|
+
payloads: ::temporalio_common::protos::temporal::api::common::v1::Payloads,
|
|
1119
|
+
converter: &::temporalio_common::data_converters::PayloadConverter,
|
|
1120
|
+
) -> Option<::futures_util::future::LocalBoxFuture<'static, Result<::temporalio_common::protos::temporal::api::common::v1::Payload, ::temporalio_sdk::workflows::WorkflowError>>> {
|
|
1121
|
+
match name {
|
|
1122
|
+
#(#dispatch_update_arms)*
|
|
1123
|
+
_ => None,
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
fn validate_update(
|
|
1128
|
+
&self,
|
|
1129
|
+
ctx: ::temporalio_sdk::WorkflowContextView,
|
|
1130
|
+
name: &str,
|
|
1131
|
+
payloads: &::temporalio_common::protos::temporal::api::common::v1::Payloads,
|
|
1132
|
+
converter: &::temporalio_common::data_converters::PayloadConverter,
|
|
1133
|
+
) -> Option<Result<(), ::temporalio_sdk::workflows::WorkflowError>> {
|
|
1134
|
+
|
|
1135
|
+
match name {
|
|
1136
|
+
#(#validate_update_arms)*
|
|
1137
|
+
_ => None,
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1143
|
+
// Generate dispatch_query match arms
|
|
1144
|
+
let dispatch_query_arms: Vec<TokenStream2> = self
|
|
1145
|
+
.queries
|
|
1146
|
+
.iter()
|
|
1147
|
+
.map(|q| {
|
|
1148
|
+
let info = HandlerCodegenInfo::new(
|
|
1149
|
+
&q.method,
|
|
1150
|
+
&q.attributes,
|
|
1151
|
+
q.input_type.as_ref(),
|
|
1152
|
+
module_ident,
|
|
1153
|
+
);
|
|
1154
|
+
let struct_ident = &info.struct_ident;
|
|
1155
|
+
let handler_name = &info.handler_name;
|
|
1156
|
+
|
|
1157
|
+
quote! {
|
|
1158
|
+
#handler_name => Some(<Self as ::temporalio_sdk::workflows::ExecutableQuery<#module_ident::#struct_ident>>::dispatch(self, &ctx, payloads, converter)),
|
|
1159
|
+
}
|
|
1160
|
+
})
|
|
1161
|
+
.collect();
|
|
1162
|
+
|
|
1163
|
+
// Generate dispatch_query only if there are queries
|
|
1164
|
+
let dispatch_query_impl = if self.queries.is_empty() {
|
|
1165
|
+
quote! {}
|
|
1166
|
+
} else {
|
|
1167
|
+
quote! {
|
|
1168
|
+
fn dispatch_query(
|
|
1169
|
+
&self,
|
|
1170
|
+
ctx: ::temporalio_sdk::WorkflowContextView,
|
|
1171
|
+
name: &str,
|
|
1172
|
+
payloads: &::temporalio_common::protos::temporal::api::common::v1::Payloads,
|
|
1173
|
+
converter: &::temporalio_common::data_converters::PayloadConverter,
|
|
1174
|
+
) -> Option<Result<::temporalio_common::protos::temporal::api::common::v1::Payload, ::temporalio_sdk::workflows::WorkflowError>> {
|
|
1175
|
+
match name {
|
|
1176
|
+
#(#dispatch_query_arms)*
|
|
1177
|
+
_ => None,
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
quote! {
|
|
1184
|
+
impl ::temporalio_sdk::workflows::WorkflowImplementation for #impl_type {
|
|
1185
|
+
type Run = #module_ident::#run_struct_ident;
|
|
1186
|
+
|
|
1187
|
+
const HAS_INIT: bool = #has_init;
|
|
1188
|
+
const INIT_TAKES_INPUT: bool = #init_has_input;
|
|
1189
|
+
|
|
1190
|
+
fn name() -> &'static str {
|
|
1191
|
+
<#impl_type>::name()
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
fn init(
|
|
1195
|
+
ctx: ::temporalio_sdk::WorkflowContextView,
|
|
1196
|
+
input: ::std::option::Option<<Self::Run as ::temporalio_common::WorkflowDefinition>::Input>,
|
|
1197
|
+
) -> Self {
|
|
1198
|
+
#init_body
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
fn run(
|
|
1202
|
+
mut ctx: ::temporalio_sdk::WorkflowContext<Self>,
|
|
1203
|
+
input: ::std::option::Option<<Self::Run as ::temporalio_common::WorkflowDefinition>::Input>,
|
|
1204
|
+
) -> ::futures_util::future::LocalBoxFuture<'static, Result<::temporalio_common::protos::temporal::api::common::v1::Payload, ::temporalio_sdk::WorkflowTermination>> {
|
|
1205
|
+
#run_impl_body
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
#dispatch_signal_impl
|
|
1209
|
+
#dispatch_update_impl
|
|
1210
|
+
#dispatch_query_impl
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
fn generate_workflow_implementer(&self, impl_type: &Type) -> TokenStream2 {
|
|
1216
|
+
quote! {
|
|
1217
|
+
impl ::temporalio_sdk::workflows::WorkflowImplementer for #impl_type {
|
|
1218
|
+
fn register_all(defs: &mut ::temporalio_sdk::workflows::WorkflowDefinitions) {
|
|
1219
|
+
defs.register_workflow_run::<Self>();
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|