@temporalio/core-bridge 1.15.0 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/Cargo.lock +172 -70
  2. package/lib/native.d.ts +1 -1
  3. package/package.json +2 -2
  4. package/releases/aarch64-apple-darwin/index.node +0 -0
  5. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  6. package/releases/x86_64-apple-darwin/index.node +0 -0
  7. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  8. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  9. package/sdk-core/.github/workflows/per-pr.yml +6 -6
  10. package/sdk-core/AGENTS.md +41 -30
  11. package/sdk-core/Cargo.toml +3 -0
  12. package/sdk-core/README.md +15 -9
  13. package/sdk-core/crates/client/Cargo.toml +4 -0
  14. package/sdk-core/crates/client/README.md +139 -0
  15. package/sdk-core/crates/client/src/async_activity_handle.rs +297 -0
  16. package/sdk-core/crates/client/src/callback_based.rs +7 -0
  17. package/sdk-core/crates/client/src/errors.rs +294 -0
  18. package/sdk-core/crates/client/src/{raw.rs → grpc.rs} +280 -159
  19. package/sdk-core/crates/client/src/lib.rs +920 -1326
  20. package/sdk-core/crates/client/src/metrics.rs +24 -33
  21. package/sdk-core/crates/client/src/options_structs.rs +457 -0
  22. package/sdk-core/crates/client/src/replaceable.rs +5 -4
  23. package/sdk-core/crates/client/src/request_extensions.rs +8 -9
  24. package/sdk-core/crates/client/src/retry.rs +99 -54
  25. package/sdk-core/crates/client/src/{worker/mod.rs → worker.rs} +1 -1
  26. package/sdk-core/crates/client/src/workflow_handle.rs +826 -0
  27. package/sdk-core/crates/common/Cargo.toml +61 -2
  28. package/sdk-core/crates/common/build.rs +742 -12
  29. package/sdk-core/crates/common/protos/api_upstream/.github/workflows/ci.yml +2 -0
  30. package/sdk-core/crates/common/protos/api_upstream/Makefile +2 -1
  31. package/sdk-core/crates/common/protos/api_upstream/buf.yaml +0 -3
  32. package/sdk-core/crates/common/protos/api_upstream/cmd/check-path-conflicts/main.go +137 -0
  33. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +1166 -770
  34. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +1243 -750
  35. package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +2 -2
  36. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +4 -3
  37. package/sdk-core/crates/common/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -0
  38. package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +4 -0
  39. package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
  40. package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +16 -1
  41. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -6
  42. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +88 -33
  43. package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +4 -2
  44. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
  45. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +5 -5
  46. package/sdk-core/crates/common/src/activity_definition.rs +20 -0
  47. package/sdk-core/crates/common/src/data_converters.rs +770 -0
  48. package/sdk-core/crates/common/src/envconfig.rs +5 -0
  49. package/sdk-core/crates/common/src/lib.rs +15 -211
  50. package/sdk-core/crates/common/src/payload_visitor.rs +648 -0
  51. package/sdk-core/crates/common/src/priority.rs +110 -0
  52. package/sdk-core/crates/common/src/protos/canned_histories.rs +3 -0
  53. package/sdk-core/crates/common/src/protos/history_builder.rs +45 -0
  54. package/sdk-core/crates/common/src/protos/history_info.rs +2 -0
  55. package/sdk-core/crates/common/src/protos/mod.rs +122 -27
  56. package/sdk-core/crates/common/src/protos/task_token.rs +3 -3
  57. package/sdk-core/crates/common/src/protos/utilities.rs +11 -0
  58. package/sdk-core/crates/{sdk-core → common}/src/telemetry/log_export.rs +5 -7
  59. package/sdk-core/crates/common/src/telemetry/metrics/core.rs +125 -0
  60. package/sdk-core/crates/common/src/telemetry/metrics.rs +268 -223
  61. package/sdk-core/crates/{sdk-core → common}/src/telemetry/otel.rs +8 -13
  62. package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_meter.rs +49 -50
  63. package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_server.rs +2 -3
  64. package/sdk-core/crates/common/src/telemetry.rs +264 -4
  65. package/sdk-core/crates/common/src/worker.rs +68 -603
  66. package/sdk-core/crates/common/src/workflow_definition.rs +60 -0
  67. package/sdk-core/crates/macros/Cargo.toml +5 -1
  68. package/sdk-core/crates/macros/src/activities_definitions.rs +585 -0
  69. package/sdk-core/crates/macros/src/fsm_impl.rs +507 -0
  70. package/sdk-core/crates/macros/src/lib.rs +138 -512
  71. package/sdk-core/crates/macros/src/macro_utils.rs +106 -0
  72. package/sdk-core/crates/macros/src/workflow_definitions.rs +1224 -0
  73. package/sdk-core/crates/sdk/Cargo.toml +19 -6
  74. package/sdk-core/crates/sdk/README.md +415 -0
  75. package/sdk-core/crates/sdk/src/activities.rs +417 -0
  76. package/sdk-core/crates/sdk/src/interceptors.rs +1 -1
  77. package/sdk-core/crates/sdk/src/lib.rs +757 -442
  78. package/sdk-core/crates/sdk/src/workflow_context/options.rs +45 -35
  79. package/sdk-core/crates/sdk/src/workflow_context.rs +1033 -289
  80. package/sdk-core/crates/sdk/src/workflow_future.rs +277 -213
  81. package/sdk-core/crates/sdk/src/workflows.rs +711 -0
  82. package/sdk-core/crates/sdk-core/Cargo.toml +57 -64
  83. package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +41 -35
  84. package/sdk-core/crates/sdk-core/machine_coverage/ActivityMachine_Coverage.puml +1 -1
  85. package/sdk-core/crates/sdk-core/src/abstractions.rs +6 -10
  86. package/sdk-core/crates/sdk-core/src/core_tests/activity_tasks.rs +6 -5
  87. package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +13 -15
  88. package/sdk-core/crates/sdk-core/src/core_tests/queries.rs +21 -25
  89. package/sdk-core/crates/sdk-core/src/core_tests/replay_flag.rs +7 -10
  90. package/sdk-core/crates/sdk-core/src/core_tests/updates.rs +14 -17
  91. package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +493 -26
  92. package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +4 -8
  93. package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +7 -7
  94. package/sdk-core/crates/sdk-core/src/histfetch.rs +20 -10
  95. package/sdk-core/crates/sdk-core/src/lib.rs +41 -111
  96. package/sdk-core/crates/sdk-core/src/pollers/mod.rs +4 -9
  97. package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +118 -19
  98. package/sdk-core/crates/sdk-core/src/protosext/mod.rs +2 -2
  99. package/sdk-core/crates/sdk-core/src/replay/mod.rs +14 -5
  100. package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +179 -196
  101. package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +3 -280
  102. package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +6 -9
  103. package/sdk-core/crates/sdk-core/src/test_help/unit_helpers.rs +3 -6
  104. package/sdk-core/crates/sdk-core/src/worker/activities/local_activities.rs +11 -14
  105. package/sdk-core/crates/sdk-core/src/worker/activities.rs +16 -19
  106. package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +9 -5
  107. package/sdk-core/crates/sdk-core/src/worker/client.rs +103 -81
  108. package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +7 -11
  109. package/sdk-core/crates/sdk-core/src/worker/mod.rs +1124 -229
  110. package/sdk-core/crates/sdk-core/src/worker/nexus.rs +145 -23
  111. package/sdk-core/crates/sdk-core/src/worker/slot_provider.rs +2 -2
  112. package/sdk-core/crates/sdk-core/src/worker/tuner/fixed_size.rs +2 -2
  113. package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +13 -13
  114. package/sdk-core/crates/sdk-core/src/worker/tuner.rs +28 -8
  115. package/sdk-core/crates/sdk-core/src/worker/workflow/driven_workflow.rs +9 -3
  116. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +21 -22
  117. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +19 -4
  118. package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +14 -18
  119. package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +4 -6
  120. package/sdk-core/crates/sdk-core/src/worker/workflow/run_cache.rs +4 -7
  121. package/sdk-core/crates/sdk-core/src/worker/workflow/wft_extraction.rs +2 -4
  122. package/sdk-core/crates/sdk-core/src/worker/workflow/wft_poller.rs +8 -9
  123. package/sdk-core/crates/sdk-core/src/worker/workflow/workflow_stream.rs +1 -3
  124. package/sdk-core/crates/sdk-core/tests/activities_procmacro.rs +6 -0
  125. package/sdk-core/crates/sdk-core/tests/activities_trybuild/basic_pass.rs +54 -0
  126. package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.rs +18 -0
  127. package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.stderr +5 -0
  128. package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.rs +14 -0
  129. package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.stderr +5 -0
  130. package/sdk-core/crates/sdk-core/tests/activities_trybuild/multi_arg_pass.rs +48 -0
  131. package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_input_pass.rs +14 -0
  132. package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_return_type_pass.rs +19 -0
  133. package/sdk-core/crates/sdk-core/tests/cloud_tests.rs +14 -5
  134. package/sdk-core/crates/sdk-core/tests/common/activity_functions.rs +55 -0
  135. package/sdk-core/crates/sdk-core/tests/common/mod.rs +241 -196
  136. package/sdk-core/crates/sdk-core/tests/common/workflows.rs +41 -28
  137. package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +3 -5
  138. package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +73 -64
  139. package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +298 -252
  140. package/sdk-core/crates/sdk-core/tests/integ_tests/async_activity_client_tests.rs +230 -0
  141. package/sdk-core/crates/sdk-core/tests/integ_tests/client_tests.rs +94 -57
  142. package/sdk-core/crates/sdk-core/tests/integ_tests/data_converter_tests.rs +381 -0
  143. package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +16 -12
  144. package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +48 -40
  145. package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +327 -255
  146. package/sdk-core/crates/sdk-core/tests/integ_tests/pagination_tests.rs +50 -45
  147. package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +147 -126
  148. package/sdk-core/crates/sdk-core/tests/integ_tests/queries_tests.rs +103 -89
  149. package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +609 -453
  150. package/sdk-core/crates/sdk-core/tests/integ_tests/visibility_tests.rs +80 -62
  151. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +360 -231
  152. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +248 -185
  153. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +52 -43
  154. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_client_tests.rs +180 -0
  155. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +428 -315
  156. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +82 -56
  157. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +56 -28
  158. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +364 -243
  159. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/client_interactions.rs +552 -0
  160. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +101 -42
  161. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +243 -147
  162. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +98 -28
  163. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1475 -1036
  164. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +73 -41
  165. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +397 -238
  166. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +414 -189
  167. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/queries.rs +415 -0
  168. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/replay.rs +96 -36
  169. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +154 -137
  170. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +183 -105
  171. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +85 -38
  172. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +142 -40
  173. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +73 -54
  174. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +363 -226
  175. package/sdk-core/crates/sdk-core/tests/main.rs +17 -15
  176. package/sdk-core/crates/sdk-core/tests/manual_tests.rs +207 -152
  177. package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +65 -34
  178. package/sdk-core/crates/sdk-core/tests/shared_tests/priority.rs +107 -84
  179. package/sdk-core/crates/sdk-core/tests/workflows_procmacro.rs +6 -0
  180. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.rs +26 -0
  181. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.stderr +5 -0
  182. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/basic_pass.rs +49 -0
  183. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/minimal_pass.rs +21 -0
  184. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.rs +26 -0
  185. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.stderr +5 -0
  186. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.rs +21 -0
  187. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.stderr +5 -0
  188. package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +7 -1
  189. package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +14 -14
  190. package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +83 -74
  191. package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +9 -14
  192. package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +1 -2
  193. package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +13 -13
  194. package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +6 -6
  195. package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +3 -4
  196. package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +62 -75
  197. package/sdk-core/rustfmt.toml +2 -1
  198. package/src/client.rs +205 -318
  199. package/src/metrics.rs +22 -30
  200. package/src/runtime.rs +4 -5
  201. package/src/worker.rs +16 -19
  202. package/ts/native.ts +1 -1
  203. package/sdk-core/crates/client/src/workflow_handle/mod.rs +0 -212
  204. package/sdk-core/crates/common/src/errors.rs +0 -85
  205. package/sdk-core/crates/common/tests/worker_task_types_test.rs +0 -129
  206. package/sdk-core/crates/sdk/src/activity_context.rs +0 -238
  207. package/sdk-core/crates/sdk/src/app_data.rs +0 -37
  208. package/sdk-core/crates/sdk-core/tests/integ_tests/activity_functions.rs +0 -5
  209. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +0 -61
@@ -1,15 +1,63 @@
1
- use std::{env, path::PathBuf};
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
- .type_attribute(
86
- ".",
87
- "#[cfg_attr(feature = \"serde_serialize\", derive(::serde::Serialize, ::serde::Deserialize))]",
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
- let mut c = Config::new();
106
- c.enable_type_names();
107
- c
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
+ }