@temporalio/core-bridge 1.15.0 → 1.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +19 -4
- 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 +414 -189
- 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 +16 -19
- 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
|
@@ -1,15 +1,63 @@
|
|
|
1
|
-
use
|
|
2
|
-
|
|
1
|
+
use prost::Message;
|
|
2
|
+
use prost_types::{
|
|
3
|
+
DescriptorProto, FieldDescriptorProto, FileDescriptorSet, MessageOptions,
|
|
4
|
+
field_descriptor_proto::{Label, Type},
|
|
5
|
+
};
|
|
6
|
+
use std::{
|
|
7
|
+
collections::{HashMap, HashSet},
|
|
8
|
+
env,
|
|
9
|
+
fs::File,
|
|
10
|
+
io::{Read, Write},
|
|
11
|
+
path::{Path, PathBuf},
|
|
12
|
+
};
|
|
3
13
|
use tonic_prost_build::Config;
|
|
4
14
|
|
|
5
15
|
static ALWAYS_SERDE: &str = "#[cfg_attr(not(feature = \"serde_serialize\"), \
|
|
6
16
|
derive(::serde::Serialize, ::serde::Deserialize))]";
|
|
7
17
|
|
|
18
|
+
static SERDE_ATTR: &str =
|
|
19
|
+
"#[cfg_attr(feature = \"serde_serialize\", derive(::serde::Serialize, ::serde::Deserialize))]";
|
|
20
|
+
|
|
21
|
+
/// Package prefixes that get conditional serde derive via type_attribute.
|
|
22
|
+
/// Packages under `temporal.api` are listed individually to exclude the pbjson packages
|
|
23
|
+
/// (temporal.api.common, temporal.api.enums, temporal.api.failure), which get their
|
|
24
|
+
/// Serialize/Deserialize impls from generated .serde.rs files.
|
|
25
|
+
///
|
|
26
|
+
/// If you add a new proto package, add its prefix here unless it is also
|
|
27
|
+
/// added to the pbjson_build list below.
|
|
28
|
+
const SERDE_DERIVE_PREFIXES: &[&str] = &[
|
|
29
|
+
".coresdk",
|
|
30
|
+
".grpc",
|
|
31
|
+
".temporal.api.activity",
|
|
32
|
+
".temporal.api.batch",
|
|
33
|
+
".temporal.api.cloud",
|
|
34
|
+
".temporal.api.command",
|
|
35
|
+
".temporal.api.deployment",
|
|
36
|
+
".temporal.api.filter",
|
|
37
|
+
".temporal.api.history",
|
|
38
|
+
".temporal.api.namespace",
|
|
39
|
+
".temporal.api.nexus",
|
|
40
|
+
".temporal.api.operatorservice",
|
|
41
|
+
".temporal.api.protocol",
|
|
42
|
+
".temporal.api.query",
|
|
43
|
+
".temporal.api.replication",
|
|
44
|
+
".temporal.api.rules",
|
|
45
|
+
".temporal.api.schedule",
|
|
46
|
+
".temporal.api.sdk",
|
|
47
|
+
".temporal.api.taskqueue",
|
|
48
|
+
".temporal.api.testservice",
|
|
49
|
+
".temporal.api.update",
|
|
50
|
+
".temporal.api.version",
|
|
51
|
+
".temporal.api.worker",
|
|
52
|
+
".temporal.api.workflow",
|
|
53
|
+
".temporal.api.workflowservice",
|
|
54
|
+
];
|
|
55
|
+
|
|
8
56
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
9
57
|
println!("cargo:rerun-if-changed=./protos");
|
|
10
58
|
let out = PathBuf::from(env::var("OUT_DIR").unwrap());
|
|
11
59
|
let descriptor_file = out.join("descriptors.bin");
|
|
12
|
-
tonic_prost_build::configure()
|
|
60
|
+
let mut builder = tonic_prost_build::configure()
|
|
13
61
|
// We don't actually want to build the grpc definitions - we don't need them (for now).
|
|
14
62
|
// Just build the message structs.
|
|
15
63
|
.build_server(false)
|
|
@@ -81,11 +129,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
81
129
|
)
|
|
82
130
|
.type_attribute("coresdk.Task.variant", "#[derive(::derive_more::From)]")
|
|
83
131
|
// All external data is useful to be able to JSON serialize, so it can render in web UI
|
|
84
|
-
.type_attribute(".coresdk.external_data", ALWAYS_SERDE)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
132
|
+
.type_attribute(".coresdk.external_data", ALWAYS_SERDE);
|
|
133
|
+
|
|
134
|
+
for prefix in SERDE_DERIVE_PREFIXES {
|
|
135
|
+
builder = builder.type_attribute(*prefix, SERDE_ATTR);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
builder
|
|
89
139
|
.field_attribute(
|
|
90
140
|
"coresdk.external_data.LocalActivityMarkerData.complete_time",
|
|
91
141
|
"#[serde(with = \"opt_timestamp\")]",
|
|
@@ -98,21 +148,23 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
98
148
|
"coresdk.external_data.LocalActivityMarkerData.backoff",
|
|
99
149
|
"#[serde(with = \"opt_duration\")]",
|
|
100
150
|
)
|
|
101
|
-
.file_descriptor_set_path(descriptor_file)
|
|
151
|
+
.file_descriptor_set_path(&descriptor_file)
|
|
102
152
|
.skip_debug(["temporal.api.common.v1.Payload"])
|
|
103
153
|
.compile_with_config(
|
|
104
154
|
{
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
155
|
+
let mut c = Config::new();
|
|
156
|
+
c.enable_type_names();
|
|
157
|
+
c
|
|
108
158
|
},
|
|
109
159
|
&[
|
|
110
160
|
"./protos/local/temporal/sdk/core/core_interface.proto",
|
|
111
161
|
"./protos/api_upstream/temporal/api/workflowservice/v1/service.proto",
|
|
112
162
|
"./protos/api_upstream/temporal/api/operatorservice/v1/service.proto",
|
|
163
|
+
"./protos/api_upstream/temporal/api/errordetails/v1/message.proto",
|
|
113
164
|
"./protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/service.proto",
|
|
114
165
|
"./protos/testsrv_upstream/temporal/api/testservice/v1/service.proto",
|
|
115
166
|
"./protos/grpc/health/v1/health.proto",
|
|
167
|
+
"./protos/google/rpc/status.proto",
|
|
116
168
|
],
|
|
117
169
|
&[
|
|
118
170
|
"./protos/api_upstream",
|
|
@@ -120,8 +172,686 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
120
172
|
"./protos/local",
|
|
121
173
|
"./protos/testsrv_upstream",
|
|
122
174
|
"./protos/grpc",
|
|
175
|
+
"./protos",
|
|
123
176
|
],
|
|
124
177
|
)?;
|
|
125
178
|
|
|
179
|
+
generate_payload_visitor(&out, &descriptor_file)?;
|
|
180
|
+
// TODO [rust-sdk-branch]: support normal JSON and proto JSON serialization
|
|
181
|
+
let descriptors = std::fs::read(&descriptor_file)?;
|
|
182
|
+
pbjson_build::Builder::new()
|
|
183
|
+
.register_descriptors(&descriptors)?
|
|
184
|
+
.build(&[
|
|
185
|
+
".temporal.api.failure",
|
|
186
|
+
".temporal.api.common",
|
|
187
|
+
".temporal.api.enums",
|
|
188
|
+
])?;
|
|
189
|
+
|
|
190
|
+
Ok(())
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// Generate PayloadVisitable implementations by parsing proto descriptors.
|
|
194
|
+
fn generate_payload_visitor(
|
|
195
|
+
out_dir: &Path,
|
|
196
|
+
descriptor_path: &Path,
|
|
197
|
+
) -> Result<(), Box<dyn std::error::Error>> {
|
|
198
|
+
let mut descriptor_bytes = Vec::new();
|
|
199
|
+
File::open(descriptor_path)?.read_to_end(&mut descriptor_bytes)?;
|
|
200
|
+
let descriptor_set = FileDescriptorSet::decode(&descriptor_bytes[..])?;
|
|
201
|
+
|
|
202
|
+
let mut generator = PayloadVisitorGenerator::new();
|
|
203
|
+
generator.process_descriptors(&descriptor_set);
|
|
204
|
+
|
|
205
|
+
let output_path = out_dir.join("payload_visitor_impl.rs");
|
|
206
|
+
let mut file = File::create(&output_path)?;
|
|
207
|
+
file.write_all(generator.generate().as_bytes())?;
|
|
208
|
+
|
|
126
209
|
Ok(())
|
|
127
210
|
}
|
|
211
|
+
|
|
212
|
+
/// Stores information about a message field that contains payloads.
|
|
213
|
+
#[derive(Debug, Clone)]
|
|
214
|
+
struct PayloadFieldInfo {
|
|
215
|
+
/// The proto field name
|
|
216
|
+
name: String,
|
|
217
|
+
/// The fully qualified proto path for the field
|
|
218
|
+
proto_path: String,
|
|
219
|
+
/// What kind of payload field this is
|
|
220
|
+
kind: PayloadFieldKind,
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
#[derive(Debug, Clone)]
|
|
224
|
+
enum PayloadFieldKind {
|
|
225
|
+
/// A singular Payload field
|
|
226
|
+
SinglePayload,
|
|
227
|
+
/// A repeated Payload field
|
|
228
|
+
RepeatedPayload,
|
|
229
|
+
/// A Payloads message field
|
|
230
|
+
PayloadsMessage,
|
|
231
|
+
/// A map with Payload values
|
|
232
|
+
MapPayload,
|
|
233
|
+
/// A map with nested message values that contain payloads
|
|
234
|
+
MapNestedMessage,
|
|
235
|
+
/// A nested message that contains payloads
|
|
236
|
+
NestedMessage,
|
|
237
|
+
/// A oneof that may contain payloads
|
|
238
|
+
Oneof {
|
|
239
|
+
/// The name of the oneof field
|
|
240
|
+
oneof_name: String,
|
|
241
|
+
/// Payload-containing variants
|
|
242
|
+
variants: Vec<OneofVariant>,
|
|
243
|
+
/// Total number of variants in the oneof (to know if we need a catch-all)
|
|
244
|
+
total_variants: usize,
|
|
245
|
+
},
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#[derive(Debug, Clone)]
|
|
249
|
+
struct OneofVariant {
|
|
250
|
+
name: String,
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/// Generator for PayloadVisitable implementations.
|
|
254
|
+
struct PayloadVisitorGenerator {
|
|
255
|
+
/// Maps fully qualified message names to their descriptors
|
|
256
|
+
messages: HashMap<String, DescriptorProto>,
|
|
257
|
+
/// Messages that contain Payloads (directly or transitively)
|
|
258
|
+
payload_containing: HashSet<String>,
|
|
259
|
+
/// Types currently being checked (for cycle detection)
|
|
260
|
+
checking: HashSet<String>,
|
|
261
|
+
/// Types that have been checked and don't contain payloads
|
|
262
|
+
not_payload_containing: HashSet<String>,
|
|
263
|
+
/// The payload fields for each message
|
|
264
|
+
message_fields: HashMap<String, Vec<PayloadFieldInfo>>,
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
impl PayloadVisitorGenerator {
|
|
268
|
+
fn new() -> Self {
|
|
269
|
+
Self {
|
|
270
|
+
messages: HashMap::new(),
|
|
271
|
+
payload_containing: HashSet::new(),
|
|
272
|
+
checking: HashSet::new(),
|
|
273
|
+
not_payload_containing: HashSet::new(),
|
|
274
|
+
message_fields: HashMap::new(),
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
fn process_descriptors(&mut self, descriptor_set: &FileDescriptorSet) {
|
|
279
|
+
// First pass: collect all message types
|
|
280
|
+
for file in &descriptor_set.file {
|
|
281
|
+
let package = file.package.as_deref().unwrap_or("");
|
|
282
|
+
for msg in &file.message_type {
|
|
283
|
+
self.collect_messages(package, msg);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Second pass: find payload-containing types
|
|
288
|
+
let all_names: Vec<String> = self.messages.keys().cloned().collect();
|
|
289
|
+
for name in &all_names {
|
|
290
|
+
self.check_contains_payload(name);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Third pass: build field info for payload-containing types
|
|
294
|
+
for name in self.payload_containing.clone() {
|
|
295
|
+
self.build_field_info(&name);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
fn collect_messages(&mut self, package: &str, msg: &DescriptorProto) {
|
|
300
|
+
let name = msg.name.as_deref().unwrap_or("");
|
|
301
|
+
let full_name = if package.is_empty() {
|
|
302
|
+
name.to_string()
|
|
303
|
+
} else {
|
|
304
|
+
format!("{}.{}", package, name)
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
self.messages.insert(full_name.clone(), msg.clone());
|
|
308
|
+
|
|
309
|
+
// Collect nested types
|
|
310
|
+
for nested in &msg.nested_type {
|
|
311
|
+
// Skip map entry types
|
|
312
|
+
if is_map_entry(&nested.options) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
self.collect_messages(&full_name, nested);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
fn check_contains_payload(&mut self, name: &str) -> bool {
|
|
320
|
+
// Already determined to contain payloads
|
|
321
|
+
if self.payload_containing.contains(name) {
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Already determined to not contain payloads
|
|
326
|
+
if self.not_payload_containing.contains(name) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Currently checking this type - break the cycle
|
|
331
|
+
if self.checking.contains(name) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Base cases
|
|
336
|
+
if name == "temporal.api.common.v1.Payload" {
|
|
337
|
+
self.payload_containing.insert(name.to_string());
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
if name == "temporal.api.common.v1.Payloads" {
|
|
341
|
+
self.payload_containing.insert(name.to_string());
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
let msg = match self.messages.get(name) {
|
|
346
|
+
Some(m) => m.clone(),
|
|
347
|
+
None => return false,
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
// Mark as currently checking
|
|
351
|
+
self.checking.insert(name.to_string());
|
|
352
|
+
|
|
353
|
+
// Check each field
|
|
354
|
+
for field in &msg.field {
|
|
355
|
+
if self.field_contains_payload(&msg, field) {
|
|
356
|
+
self.checking.remove(name);
|
|
357
|
+
self.payload_containing.insert(name.to_string());
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Done checking - doesn't contain payloads
|
|
363
|
+
self.checking.remove(name);
|
|
364
|
+
self.not_payload_containing.insert(name.to_string());
|
|
365
|
+
false
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
fn field_contains_payload(
|
|
369
|
+
&mut self,
|
|
370
|
+
msg: &DescriptorProto,
|
|
371
|
+
field: &FieldDescriptorProto,
|
|
372
|
+
) -> bool {
|
|
373
|
+
if is_message_type(field) {
|
|
374
|
+
let type_name = field.type_name.as_deref().unwrap_or("");
|
|
375
|
+
let type_name = type_name.trim_start_matches('.');
|
|
376
|
+
|
|
377
|
+
// Check if this is a map type
|
|
378
|
+
if let Some(nested) = msg.nested_type.iter().find(|n| {
|
|
379
|
+
is_map_entry(&n.options)
|
|
380
|
+
&& n.name.as_deref()
|
|
381
|
+
== Some(&Self::to_map_entry_name(
|
|
382
|
+
field.name.as_deref().unwrap_or(""),
|
|
383
|
+
))
|
|
384
|
+
}) {
|
|
385
|
+
// It's a map - check the value type
|
|
386
|
+
if let Some(value_field) = nested
|
|
387
|
+
.field
|
|
388
|
+
.iter()
|
|
389
|
+
.find(|f| f.name.as_deref() == Some("value"))
|
|
390
|
+
{
|
|
391
|
+
let value_type = value_field
|
|
392
|
+
.type_name
|
|
393
|
+
.as_deref()
|
|
394
|
+
.unwrap_or("")
|
|
395
|
+
.trim_start_matches('.');
|
|
396
|
+
return self.check_contains_payload(value_type);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return self.check_contains_payload(type_name);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
false
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
fn to_map_entry_name(field_name: &str) -> String {
|
|
407
|
+
let mut result = String::new();
|
|
408
|
+
let mut capitalize_next = true;
|
|
409
|
+
for c in field_name.chars() {
|
|
410
|
+
if c == '_' {
|
|
411
|
+
capitalize_next = true;
|
|
412
|
+
} else if capitalize_next {
|
|
413
|
+
result.push(c.to_ascii_uppercase());
|
|
414
|
+
capitalize_next = false;
|
|
415
|
+
} else {
|
|
416
|
+
result.push(c);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
result.push_str("Entry");
|
|
420
|
+
result
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
fn build_field_info(&mut self, name: &str) {
|
|
424
|
+
if self.message_fields.contains_key(name) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Skip Payload and Payloads - they are leaf types
|
|
429
|
+
if name == "temporal.api.common.v1.Payload" || name == "temporal.api.common.v1.Payloads" {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
let msg = match self.messages.get(name) {
|
|
434
|
+
Some(m) => m.clone(),
|
|
435
|
+
None => return,
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
let mut fields = Vec::new();
|
|
439
|
+
|
|
440
|
+
// Group fields by oneof
|
|
441
|
+
let mut oneof_fields: HashMap<i32, Vec<&FieldDescriptorProto>> = HashMap::new();
|
|
442
|
+
let mut regular_fields: Vec<&FieldDescriptorProto> = Vec::new();
|
|
443
|
+
|
|
444
|
+
for field in &msg.field {
|
|
445
|
+
if let Some(oneof_index) = field.oneof_index {
|
|
446
|
+
oneof_fields.entry(oneof_index).or_default().push(field);
|
|
447
|
+
} else {
|
|
448
|
+
regular_fields.push(field);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Process regular fields
|
|
453
|
+
for field in regular_fields {
|
|
454
|
+
if let Some(info) = self.build_single_field_info(name, &msg, field) {
|
|
455
|
+
fields.push(info);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Process oneofs
|
|
460
|
+
for (oneof_index, oneof_field_list) in oneof_fields {
|
|
461
|
+
let oneof_desc = &msg.oneof_decl[oneof_index as usize];
|
|
462
|
+
let oneof_name = oneof_desc.name.as_deref().unwrap_or("");
|
|
463
|
+
|
|
464
|
+
let total_variants = oneof_field_list.len();
|
|
465
|
+
let mut variants = Vec::new();
|
|
466
|
+
for field in oneof_field_list {
|
|
467
|
+
if is_message_type(field) {
|
|
468
|
+
let type_name = field
|
|
469
|
+
.type_name
|
|
470
|
+
.as_deref()
|
|
471
|
+
.unwrap_or("")
|
|
472
|
+
.trim_start_matches('.');
|
|
473
|
+
if self.payload_containing.contains(type_name) {
|
|
474
|
+
variants.push(OneofVariant {
|
|
475
|
+
name: field.name.clone().unwrap_or_default(),
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if !variants.is_empty() {
|
|
482
|
+
fields.push(PayloadFieldInfo {
|
|
483
|
+
name: oneof_name.to_string(),
|
|
484
|
+
proto_path: format!("{}.{}", name, oneof_name),
|
|
485
|
+
kind: PayloadFieldKind::Oneof {
|
|
486
|
+
oneof_name: oneof_name.to_string(),
|
|
487
|
+
variants,
|
|
488
|
+
total_variants,
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
self.message_fields.insert(name.to_string(), fields);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
fn build_single_field_info(
|
|
498
|
+
&self,
|
|
499
|
+
parent_name: &str,
|
|
500
|
+
parent_msg: &DescriptorProto,
|
|
501
|
+
field: &FieldDescriptorProto,
|
|
502
|
+
) -> Option<PayloadFieldInfo> {
|
|
503
|
+
let field_name = field.name.as_deref().unwrap_or("");
|
|
504
|
+
let proto_path = format!("{}.{}", parent_name, field_name);
|
|
505
|
+
|
|
506
|
+
if !is_message_type(field) {
|
|
507
|
+
return None;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
let type_name = field
|
|
511
|
+
.type_name
|
|
512
|
+
.as_deref()
|
|
513
|
+
.unwrap_or("")
|
|
514
|
+
.trim_start_matches('.');
|
|
515
|
+
|
|
516
|
+
// Check if it's a map
|
|
517
|
+
if let Some(nested) = parent_msg.nested_type.iter().find(|n| {
|
|
518
|
+
is_map_entry(&n.options)
|
|
519
|
+
&& n.name.as_deref() == Some(&Self::to_map_entry_name(field_name))
|
|
520
|
+
}) {
|
|
521
|
+
let value_field = nested
|
|
522
|
+
.field
|
|
523
|
+
.iter()
|
|
524
|
+
.find(|f| f.name.as_deref() == Some("value"))?;
|
|
525
|
+
let value_type = value_field
|
|
526
|
+
.type_name
|
|
527
|
+
.as_deref()
|
|
528
|
+
.unwrap_or("")
|
|
529
|
+
.trim_start_matches('.');
|
|
530
|
+
|
|
531
|
+
if !self.payload_containing.contains(value_type) {
|
|
532
|
+
return None;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if value_type == "temporal.api.common.v1.Payload" {
|
|
536
|
+
return Some(PayloadFieldInfo {
|
|
537
|
+
name: field_name.to_string(),
|
|
538
|
+
proto_path,
|
|
539
|
+
kind: PayloadFieldKind::MapPayload,
|
|
540
|
+
});
|
|
541
|
+
} else {
|
|
542
|
+
return Some(PayloadFieldInfo {
|
|
543
|
+
name: field_name.to_string(),
|
|
544
|
+
proto_path,
|
|
545
|
+
kind: PayloadFieldKind::MapNestedMessage,
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if !self.payload_containing.contains(type_name) {
|
|
551
|
+
return None;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
let is_repeated = is_repeated(field);
|
|
555
|
+
|
|
556
|
+
if type_name == "temporal.api.common.v1.Payload" {
|
|
557
|
+
Some(PayloadFieldInfo {
|
|
558
|
+
name: field_name.to_string(),
|
|
559
|
+
proto_path,
|
|
560
|
+
kind: if is_repeated {
|
|
561
|
+
PayloadFieldKind::RepeatedPayload
|
|
562
|
+
} else {
|
|
563
|
+
PayloadFieldKind::SinglePayload
|
|
564
|
+
},
|
|
565
|
+
})
|
|
566
|
+
} else if type_name == "temporal.api.common.v1.Payloads" {
|
|
567
|
+
Some(PayloadFieldInfo {
|
|
568
|
+
name: field_name.to_string(),
|
|
569
|
+
proto_path,
|
|
570
|
+
kind: PayloadFieldKind::PayloadsMessage,
|
|
571
|
+
})
|
|
572
|
+
} else {
|
|
573
|
+
Some(PayloadFieldInfo {
|
|
574
|
+
name: field_name.to_string(),
|
|
575
|
+
proto_path,
|
|
576
|
+
kind: PayloadFieldKind::NestedMessage,
|
|
577
|
+
})
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
fn generate(&self) -> String {
|
|
582
|
+
let mut output = String::new();
|
|
583
|
+
output.push_str("// Generated from descriptors.bin - DO NOT EDIT\n\n");
|
|
584
|
+
|
|
585
|
+
// Generate impls for each payload-containing type
|
|
586
|
+
for name in self.payload_containing.iter() {
|
|
587
|
+
if name == "temporal.api.common.v1.Payload" || name == "temporal.api.common.v1.Payloads"
|
|
588
|
+
{
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
if let Some(fields) = self.message_fields.get(name) {
|
|
592
|
+
output.push_str(&self.generate_impl(name, fields));
|
|
593
|
+
output.push('\n');
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
output
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
fn generate_impl(&self, proto_name: &str, fields: &[PayloadFieldInfo]) -> String {
|
|
601
|
+
let rust_path = self.proto_to_rust_path(proto_name);
|
|
602
|
+
|
|
603
|
+
let mut impl_body = String::new();
|
|
604
|
+
|
|
605
|
+
for field in fields {
|
|
606
|
+
impl_body.push_str(&self.generate_field_visit(
|
|
607
|
+
&field.name,
|
|
608
|
+
&field.proto_path,
|
|
609
|
+
&field.kind,
|
|
610
|
+
));
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
format!(
|
|
614
|
+
r#"#[allow(deprecated, clippy::single_match, clippy::collapsible_match)]
|
|
615
|
+
impl crate::payload_visitor::PayloadVisitable for {rust_path} {{
|
|
616
|
+
fn visit_payloads_mut<'a>(
|
|
617
|
+
&'a mut self,
|
|
618
|
+
visitor: &'a mut (dyn crate::payload_visitor::AsyncPayloadVisitor + Send),
|
|
619
|
+
) -> futures::future::BoxFuture<'a, ()> {{
|
|
620
|
+
Box::pin(async move {{
|
|
621
|
+
{impl_body} }})
|
|
622
|
+
}}
|
|
623
|
+
}}
|
|
624
|
+
"#,
|
|
625
|
+
rust_path = rust_path,
|
|
626
|
+
impl_body = impl_body
|
|
627
|
+
)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
fn generate_field_visit(
|
|
631
|
+
&self,
|
|
632
|
+
field_name: &str,
|
|
633
|
+
proto_path: &str,
|
|
634
|
+
kind: &PayloadFieldKind,
|
|
635
|
+
) -> String {
|
|
636
|
+
let rust_field = Self::to_snake_case(field_name);
|
|
637
|
+
|
|
638
|
+
match kind {
|
|
639
|
+
PayloadFieldKind::SinglePayload => {
|
|
640
|
+
format!(
|
|
641
|
+
r#" if let Some(payload) = &mut self.{field} {{
|
|
642
|
+
visitor.visit(crate::payload_visitor::PayloadField {{
|
|
643
|
+
path: "{path}",
|
|
644
|
+
data: crate::payload_visitor::PayloadFieldData::Single(payload),
|
|
645
|
+
}}).await;
|
|
646
|
+
}}
|
|
647
|
+
"#,
|
|
648
|
+
field = rust_field,
|
|
649
|
+
path = proto_path
|
|
650
|
+
)
|
|
651
|
+
}
|
|
652
|
+
PayloadFieldKind::RepeatedPayload => {
|
|
653
|
+
format!(
|
|
654
|
+
r#" visitor.visit(crate::payload_visitor::PayloadField {{
|
|
655
|
+
path: "{path}",
|
|
656
|
+
data: crate::payload_visitor::PayloadFieldData::Repeated(&mut self.{field}),
|
|
657
|
+
}}).await;
|
|
658
|
+
"#,
|
|
659
|
+
field = rust_field,
|
|
660
|
+
path = proto_path
|
|
661
|
+
)
|
|
662
|
+
}
|
|
663
|
+
PayloadFieldKind::PayloadsMessage => {
|
|
664
|
+
format!(
|
|
665
|
+
r#" if let Some(payloads) = &mut self.{field} {{
|
|
666
|
+
visitor.visit(crate::payload_visitor::PayloadField {{
|
|
667
|
+
path: "{path}",
|
|
668
|
+
data: crate::payload_visitor::PayloadFieldData::Payloads(payloads),
|
|
669
|
+
}}).await;
|
|
670
|
+
}}
|
|
671
|
+
"#,
|
|
672
|
+
field = rust_field,
|
|
673
|
+
path = proto_path
|
|
674
|
+
)
|
|
675
|
+
}
|
|
676
|
+
PayloadFieldKind::MapPayload => {
|
|
677
|
+
format!(
|
|
678
|
+
r#" for payload in self.{field}.values_mut() {{
|
|
679
|
+
visitor.visit(crate::payload_visitor::PayloadField {{
|
|
680
|
+
path: "{path}",
|
|
681
|
+
data: crate::payload_visitor::PayloadFieldData::Single(payload),
|
|
682
|
+
}}).await;
|
|
683
|
+
}}
|
|
684
|
+
"#,
|
|
685
|
+
field = rust_field,
|
|
686
|
+
path = proto_path
|
|
687
|
+
)
|
|
688
|
+
}
|
|
689
|
+
PayloadFieldKind::MapNestedMessage => {
|
|
690
|
+
format!(
|
|
691
|
+
r#" for item in self.{field}.values_mut() {{
|
|
692
|
+
item.visit_payloads_mut(visitor).await;
|
|
693
|
+
}}
|
|
694
|
+
"#,
|
|
695
|
+
field = rust_field
|
|
696
|
+
)
|
|
697
|
+
}
|
|
698
|
+
PayloadFieldKind::NestedMessage => {
|
|
699
|
+
// Check if the field in the parent is repeated
|
|
700
|
+
let parent_name = proto_path.rsplit_once('.').map(|(p, _)| p).unwrap_or("");
|
|
701
|
+
let is_field_repeated = if let Some(msg) = self.messages.get(parent_name) {
|
|
702
|
+
msg.field
|
|
703
|
+
.iter()
|
|
704
|
+
.any(|f| f.name.as_deref() == Some(field_name) && is_repeated(f))
|
|
705
|
+
} else {
|
|
706
|
+
false
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
if is_field_repeated {
|
|
710
|
+
format!(
|
|
711
|
+
r#" for item in &mut self.{field} {{
|
|
712
|
+
item.visit_payloads_mut(visitor).await;
|
|
713
|
+
}}
|
|
714
|
+
"#,
|
|
715
|
+
field = rust_field
|
|
716
|
+
)
|
|
717
|
+
} else {
|
|
718
|
+
format!(
|
|
719
|
+
r#" if let Some(msg) = &mut self.{field} {{
|
|
720
|
+
msg.visit_payloads_mut(visitor).await;
|
|
721
|
+
}}
|
|
722
|
+
"#,
|
|
723
|
+
field = rust_field
|
|
724
|
+
)
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
PayloadFieldKind::Oneof {
|
|
728
|
+
oneof_name,
|
|
729
|
+
variants,
|
|
730
|
+
total_variants,
|
|
731
|
+
} => {
|
|
732
|
+
// Compute the parent proto name from the proto_path
|
|
733
|
+
let parent_proto_name = proto_path.rsplit_once('.').map(|(p, _)| p).unwrap_or("");
|
|
734
|
+
// Get the full rust path to the oneof enum
|
|
735
|
+
let enum_path = self.proto_to_rust_oneof_enum_path(parent_proto_name, oneof_name);
|
|
736
|
+
// The field in the struct is snake_case of the oneof field name
|
|
737
|
+
let rust_field = Self::to_snake_case(oneof_name);
|
|
738
|
+
|
|
739
|
+
let mut arms = String::new();
|
|
740
|
+
|
|
741
|
+
for variant in variants {
|
|
742
|
+
let variant_name = Self::to_pascal_case(&variant.name);
|
|
743
|
+
arms.push_str(&format!(
|
|
744
|
+
" {enum_path}::{variant}(msg) => msg.visit_payloads_mut(visitor).await,\n",
|
|
745
|
+
enum_path = enum_path,
|
|
746
|
+
variant = variant_name
|
|
747
|
+
));
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if arms.is_empty() {
|
|
751
|
+
return String::new();
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Only add catch-all if not all variants are payload-containing
|
|
755
|
+
let catch_all = if variants.len() < *total_variants {
|
|
756
|
+
" _ => {}\n"
|
|
757
|
+
} else {
|
|
758
|
+
""
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
format!(
|
|
762
|
+
r#" if let Some({field}) = &mut self.{field} {{
|
|
763
|
+
match {field} {{
|
|
764
|
+
{arms}{catch_all} }}
|
|
765
|
+
}}
|
|
766
|
+
"#,
|
|
767
|
+
field = rust_field,
|
|
768
|
+
arms = arms,
|
|
769
|
+
catch_all = catch_all
|
|
770
|
+
)
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
fn proto_to_rust_path(&self, proto_name: &str) -> String {
|
|
776
|
+
let parts: Vec<&str> = proto_name.split('.').collect();
|
|
777
|
+
let mut rust_parts = Vec::new();
|
|
778
|
+
|
|
779
|
+
// Handle the package -> module mapping
|
|
780
|
+
for (i, part) in parts.iter().enumerate() {
|
|
781
|
+
if i == parts.len() - 1 {
|
|
782
|
+
// Last part is the type name - keep PascalCase
|
|
783
|
+
rust_parts.push((*part).to_string());
|
|
784
|
+
} else {
|
|
785
|
+
// Package parts become snake_case modules
|
|
786
|
+
rust_parts.push(Self::to_snake_case(part));
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// The protos module structure
|
|
791
|
+
let path = rust_parts.join("::");
|
|
792
|
+
|
|
793
|
+
// Map to the actual crate paths
|
|
794
|
+
format!("crate::protos::{}", path)
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
fn proto_to_rust_oneof_enum_path(&self, parent_proto_name: &str, oneof_name: &str) -> String {
|
|
798
|
+
let parts: Vec<&str> = parent_proto_name.split('.').collect();
|
|
799
|
+
let mut rust_parts = Vec::new();
|
|
800
|
+
|
|
801
|
+
// All parts become snake_case modules (struct name becomes a module containing the enum)
|
|
802
|
+
for part in parts.iter() {
|
|
803
|
+
rust_parts.push(Self::to_snake_case(part));
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
let module_path = rust_parts.join("::");
|
|
807
|
+
// The enum name is PascalCase of the oneof field name
|
|
808
|
+
let enum_name = Self::to_pascal_case(oneof_name);
|
|
809
|
+
|
|
810
|
+
format!("crate::protos::{}::{}", module_path, enum_name)
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
fn to_snake_case(s: &str) -> String {
|
|
814
|
+
let mut result = String::new();
|
|
815
|
+
for (i, c) in s.chars().enumerate() {
|
|
816
|
+
if c.is_uppercase() {
|
|
817
|
+
if i > 0 {
|
|
818
|
+
result.push('_');
|
|
819
|
+
}
|
|
820
|
+
result.push(c.to_ascii_lowercase());
|
|
821
|
+
} else {
|
|
822
|
+
result.push(c);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
result
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
fn to_pascal_case(s: &str) -> String {
|
|
829
|
+
let mut result = String::new();
|
|
830
|
+
let mut capitalize_next = true;
|
|
831
|
+
for c in s.chars() {
|
|
832
|
+
if c == '_' {
|
|
833
|
+
capitalize_next = true;
|
|
834
|
+
} else if capitalize_next {
|
|
835
|
+
result.push(c.to_ascii_uppercase());
|
|
836
|
+
capitalize_next = false;
|
|
837
|
+
} else {
|
|
838
|
+
result.push(c);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
result
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
fn is_message_type(field: &FieldDescriptorProto) -> bool {
|
|
846
|
+
field.r#type == Some(Type::Message as i32)
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
fn is_repeated(field: &FieldDescriptorProto) -> bool {
|
|
850
|
+
field.label == Some(Label::Repeated as i32)
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
fn is_map_entry(options: &Option<MessageOptions>) -> bool {
|
|
854
|
+
options
|
|
855
|
+
.as_ref()
|
|
856
|
+
.is_some_and(|o| o.map_entry.unwrap_or(false))
|
|
857
|
+
}
|