@temporalio/core-bridge 1.15.0 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/Cargo.lock +172 -70
  2. package/lib/native.d.ts +1 -1
  3. package/package.json +2 -2
  4. package/releases/aarch64-apple-darwin/index.node +0 -0
  5. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  6. package/releases/x86_64-apple-darwin/index.node +0 -0
  7. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  8. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  9. package/sdk-core/.github/workflows/per-pr.yml +6 -6
  10. package/sdk-core/AGENTS.md +41 -30
  11. package/sdk-core/Cargo.toml +3 -0
  12. package/sdk-core/README.md +15 -9
  13. package/sdk-core/crates/client/Cargo.toml +4 -0
  14. package/sdk-core/crates/client/README.md +139 -0
  15. package/sdk-core/crates/client/src/async_activity_handle.rs +297 -0
  16. package/sdk-core/crates/client/src/callback_based.rs +7 -0
  17. package/sdk-core/crates/client/src/errors.rs +294 -0
  18. package/sdk-core/crates/client/src/{raw.rs → grpc.rs} +280 -159
  19. package/sdk-core/crates/client/src/lib.rs +920 -1326
  20. package/sdk-core/crates/client/src/metrics.rs +24 -33
  21. package/sdk-core/crates/client/src/options_structs.rs +457 -0
  22. package/sdk-core/crates/client/src/replaceable.rs +5 -4
  23. package/sdk-core/crates/client/src/request_extensions.rs +8 -9
  24. package/sdk-core/crates/client/src/retry.rs +99 -54
  25. package/sdk-core/crates/client/src/{worker/mod.rs → worker.rs} +1 -1
  26. package/sdk-core/crates/client/src/workflow_handle.rs +826 -0
  27. package/sdk-core/crates/common/Cargo.toml +61 -2
  28. package/sdk-core/crates/common/build.rs +742 -12
  29. package/sdk-core/crates/common/protos/api_upstream/.github/workflows/ci.yml +2 -0
  30. package/sdk-core/crates/common/protos/api_upstream/Makefile +2 -1
  31. package/sdk-core/crates/common/protos/api_upstream/buf.yaml +0 -3
  32. package/sdk-core/crates/common/protos/api_upstream/cmd/check-path-conflicts/main.go +137 -0
  33. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +1166 -770
  34. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +1243 -750
  35. package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +2 -2
  36. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +4 -3
  37. package/sdk-core/crates/common/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -0
  38. package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +4 -0
  39. package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
  40. package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +16 -1
  41. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -6
  42. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +88 -33
  43. package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +4 -2
  44. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
  45. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +5 -5
  46. package/sdk-core/crates/common/src/activity_definition.rs +20 -0
  47. package/sdk-core/crates/common/src/data_converters.rs +770 -0
  48. package/sdk-core/crates/common/src/envconfig.rs +5 -0
  49. package/sdk-core/crates/common/src/lib.rs +15 -211
  50. package/sdk-core/crates/common/src/payload_visitor.rs +648 -0
  51. package/sdk-core/crates/common/src/priority.rs +110 -0
  52. package/sdk-core/crates/common/src/protos/canned_histories.rs +3 -0
  53. package/sdk-core/crates/common/src/protos/history_builder.rs +45 -0
  54. package/sdk-core/crates/common/src/protos/history_info.rs +2 -0
  55. package/sdk-core/crates/common/src/protos/mod.rs +122 -27
  56. package/sdk-core/crates/common/src/protos/task_token.rs +3 -3
  57. package/sdk-core/crates/common/src/protos/utilities.rs +11 -0
  58. package/sdk-core/crates/{sdk-core → common}/src/telemetry/log_export.rs +5 -7
  59. package/sdk-core/crates/common/src/telemetry/metrics/core.rs +125 -0
  60. package/sdk-core/crates/common/src/telemetry/metrics.rs +268 -223
  61. package/sdk-core/crates/{sdk-core → common}/src/telemetry/otel.rs +8 -13
  62. package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_meter.rs +49 -50
  63. package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_server.rs +2 -3
  64. package/sdk-core/crates/common/src/telemetry.rs +264 -4
  65. package/sdk-core/crates/common/src/worker.rs +68 -603
  66. package/sdk-core/crates/common/src/workflow_definition.rs +60 -0
  67. package/sdk-core/crates/macros/Cargo.toml +5 -1
  68. package/sdk-core/crates/macros/src/activities_definitions.rs +585 -0
  69. package/sdk-core/crates/macros/src/fsm_impl.rs +507 -0
  70. package/sdk-core/crates/macros/src/lib.rs +138 -512
  71. package/sdk-core/crates/macros/src/macro_utils.rs +106 -0
  72. package/sdk-core/crates/macros/src/workflow_definitions.rs +1224 -0
  73. package/sdk-core/crates/sdk/Cargo.toml +19 -6
  74. package/sdk-core/crates/sdk/README.md +415 -0
  75. package/sdk-core/crates/sdk/src/activities.rs +417 -0
  76. package/sdk-core/crates/sdk/src/interceptors.rs +1 -1
  77. package/sdk-core/crates/sdk/src/lib.rs +757 -442
  78. package/sdk-core/crates/sdk/src/workflow_context/options.rs +45 -35
  79. package/sdk-core/crates/sdk/src/workflow_context.rs +1033 -289
  80. package/sdk-core/crates/sdk/src/workflow_future.rs +277 -213
  81. package/sdk-core/crates/sdk/src/workflows.rs +711 -0
  82. package/sdk-core/crates/sdk-core/Cargo.toml +57 -64
  83. package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +41 -35
  84. package/sdk-core/crates/sdk-core/machine_coverage/ActivityMachine_Coverage.puml +1 -1
  85. package/sdk-core/crates/sdk-core/src/abstractions.rs +6 -10
  86. package/sdk-core/crates/sdk-core/src/core_tests/activity_tasks.rs +6 -5
  87. package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +13 -15
  88. package/sdk-core/crates/sdk-core/src/core_tests/queries.rs +21 -25
  89. package/sdk-core/crates/sdk-core/src/core_tests/replay_flag.rs +7 -10
  90. package/sdk-core/crates/sdk-core/src/core_tests/updates.rs +14 -17
  91. package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +493 -26
  92. package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +4 -8
  93. package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +7 -7
  94. package/sdk-core/crates/sdk-core/src/histfetch.rs +20 -10
  95. package/sdk-core/crates/sdk-core/src/lib.rs +41 -111
  96. package/sdk-core/crates/sdk-core/src/pollers/mod.rs +4 -9
  97. package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +118 -19
  98. package/sdk-core/crates/sdk-core/src/protosext/mod.rs +2 -2
  99. package/sdk-core/crates/sdk-core/src/replay/mod.rs +14 -5
  100. package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +179 -196
  101. package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +3 -280
  102. package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +6 -9
  103. package/sdk-core/crates/sdk-core/src/test_help/unit_helpers.rs +3 -6
  104. package/sdk-core/crates/sdk-core/src/worker/activities/local_activities.rs +11 -14
  105. package/sdk-core/crates/sdk-core/src/worker/activities.rs +16 -19
  106. package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +9 -5
  107. package/sdk-core/crates/sdk-core/src/worker/client.rs +103 -81
  108. package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +7 -11
  109. package/sdk-core/crates/sdk-core/src/worker/mod.rs +1124 -229
  110. package/sdk-core/crates/sdk-core/src/worker/nexus.rs +145 -23
  111. package/sdk-core/crates/sdk-core/src/worker/slot_provider.rs +2 -2
  112. package/sdk-core/crates/sdk-core/src/worker/tuner/fixed_size.rs +2 -2
  113. package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +13 -13
  114. package/sdk-core/crates/sdk-core/src/worker/tuner.rs +28 -8
  115. package/sdk-core/crates/sdk-core/src/worker/workflow/driven_workflow.rs +9 -3
  116. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +21 -22
  117. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +19 -4
  118. package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +14 -18
  119. package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +4 -6
  120. package/sdk-core/crates/sdk-core/src/worker/workflow/run_cache.rs +4 -7
  121. package/sdk-core/crates/sdk-core/src/worker/workflow/wft_extraction.rs +2 -4
  122. package/sdk-core/crates/sdk-core/src/worker/workflow/wft_poller.rs +8 -9
  123. package/sdk-core/crates/sdk-core/src/worker/workflow/workflow_stream.rs +1 -3
  124. package/sdk-core/crates/sdk-core/tests/activities_procmacro.rs +6 -0
  125. package/sdk-core/crates/sdk-core/tests/activities_trybuild/basic_pass.rs +54 -0
  126. package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.rs +18 -0
  127. package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.stderr +5 -0
  128. package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.rs +14 -0
  129. package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.stderr +5 -0
  130. package/sdk-core/crates/sdk-core/tests/activities_trybuild/multi_arg_pass.rs +48 -0
  131. package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_input_pass.rs +14 -0
  132. package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_return_type_pass.rs +19 -0
  133. package/sdk-core/crates/sdk-core/tests/cloud_tests.rs +14 -5
  134. package/sdk-core/crates/sdk-core/tests/common/activity_functions.rs +55 -0
  135. package/sdk-core/crates/sdk-core/tests/common/mod.rs +241 -196
  136. package/sdk-core/crates/sdk-core/tests/common/workflows.rs +41 -28
  137. package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +3 -5
  138. package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +73 -64
  139. package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +298 -252
  140. package/sdk-core/crates/sdk-core/tests/integ_tests/async_activity_client_tests.rs +230 -0
  141. package/sdk-core/crates/sdk-core/tests/integ_tests/client_tests.rs +94 -57
  142. package/sdk-core/crates/sdk-core/tests/integ_tests/data_converter_tests.rs +381 -0
  143. package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +16 -12
  144. package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +48 -40
  145. package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +327 -255
  146. package/sdk-core/crates/sdk-core/tests/integ_tests/pagination_tests.rs +50 -45
  147. package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +147 -126
  148. package/sdk-core/crates/sdk-core/tests/integ_tests/queries_tests.rs +103 -89
  149. package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +609 -453
  150. package/sdk-core/crates/sdk-core/tests/integ_tests/visibility_tests.rs +80 -62
  151. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +360 -231
  152. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +248 -185
  153. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +52 -43
  154. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_client_tests.rs +180 -0
  155. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +428 -315
  156. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +82 -56
  157. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +56 -28
  158. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +364 -243
  159. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/client_interactions.rs +552 -0
  160. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +101 -42
  161. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +243 -147
  162. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +98 -28
  163. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1475 -1036
  164. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +73 -41
  165. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +397 -238
  166. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +414 -189
  167. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/queries.rs +415 -0
  168. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/replay.rs +96 -36
  169. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +154 -137
  170. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +183 -105
  171. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +85 -38
  172. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +142 -40
  173. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +73 -54
  174. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +363 -226
  175. package/sdk-core/crates/sdk-core/tests/main.rs +17 -15
  176. package/sdk-core/crates/sdk-core/tests/manual_tests.rs +207 -152
  177. package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +65 -34
  178. package/sdk-core/crates/sdk-core/tests/shared_tests/priority.rs +107 -84
  179. package/sdk-core/crates/sdk-core/tests/workflows_procmacro.rs +6 -0
  180. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.rs +26 -0
  181. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.stderr +5 -0
  182. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/basic_pass.rs +49 -0
  183. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/minimal_pass.rs +21 -0
  184. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.rs +26 -0
  185. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.stderr +5 -0
  186. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.rs +21 -0
  187. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.stderr +5 -0
  188. package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +7 -1
  189. package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +14 -14
  190. package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +83 -74
  191. package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +9 -14
  192. package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +1 -2
  193. package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +13 -13
  194. package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +6 -6
  195. package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +3 -4
  196. package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +62 -75
  197. package/sdk-core/rustfmt.toml +2 -1
  198. package/src/client.rs +205 -318
  199. package/src/metrics.rs +22 -30
  200. package/src/runtime.rs +4 -5
  201. package/src/worker.rs +16 -19
  202. package/ts/native.ts +1 -1
  203. package/sdk-core/crates/client/src/workflow_handle/mod.rs +0 -212
  204. package/sdk-core/crates/common/src/errors.rs +0 -85
  205. package/sdk-core/crates/common/tests/worker_task_types_test.rs +0 -129
  206. package/sdk-core/crates/sdk/src/activity_context.rs +0 -238
  207. package/sdk-core/crates/sdk/src/app_data.rs +0 -37
  208. package/sdk-core/crates/sdk-core/tests/integ_tests/activity_functions.rs +0 -5
  209. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +0 -61
@@ -0,0 +1,770 @@
1
+ //! Contains traits for and default implementations of data converters, codecs, and other
2
+ //! serialization related functionality.
3
+
4
+ use crate::protos::temporal::api::{common::v1::Payload, failure::v1::Failure};
5
+ use futures::{FutureExt, future::BoxFuture};
6
+ use std::{collections::HashMap, sync::Arc};
7
+
8
+ /// Combines a [`PayloadConverter`], [`FailureConverter`], and [`PayloadCodec`] to handle all
9
+ /// serialization needs for communicating with the Temporal server.
10
+ #[derive(Clone)]
11
+ pub struct DataConverter {
12
+ payload_converter: PayloadConverter,
13
+ #[allow(dead_code)] // Will be used for failure conversion
14
+ failure_converter: Arc<dyn FailureConverter + Send + Sync>,
15
+ codec: Arc<dyn PayloadCodec + Send + Sync>,
16
+ }
17
+
18
+ impl std::fmt::Debug for DataConverter {
19
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20
+ f.debug_struct("DataConverter")
21
+ .field("payload_converter", &self.payload_converter)
22
+ .finish_non_exhaustive()
23
+ }
24
+ }
25
+ impl DataConverter {
26
+ /// Create a new DataConverter with the given payload converter, failure converter, and codec.
27
+ pub fn new(
28
+ payload_converter: PayloadConverter,
29
+ failure_converter: impl FailureConverter + Send + Sync + 'static,
30
+ codec: impl PayloadCodec + Send + Sync + 'static,
31
+ ) -> Self {
32
+ Self {
33
+ payload_converter,
34
+ failure_converter: Arc::new(failure_converter),
35
+ codec: Arc::new(codec),
36
+ }
37
+ }
38
+
39
+ /// Serialize a value into a single payload, applying the codec.
40
+ pub async fn to_payload<T: TemporalSerializable + 'static>(
41
+ &self,
42
+ data: &SerializationContextData,
43
+ val: &T,
44
+ ) -> Result<Payload, PayloadConversionError> {
45
+ let context = SerializationContext {
46
+ data,
47
+ converter: &self.payload_converter,
48
+ };
49
+ let payload = self.payload_converter.to_payload(&context, val)?;
50
+ let encoded = self.codec.encode(data, vec![payload]).await;
51
+ encoded
52
+ .into_iter()
53
+ .next()
54
+ .ok_or(PayloadConversionError::WrongEncoding)
55
+ }
56
+
57
+ /// Deserialize a value from a single payload, applying the codec.
58
+ pub async fn from_payload<T: TemporalDeserializable + 'static>(
59
+ &self,
60
+ data: &SerializationContextData,
61
+ payload: Payload,
62
+ ) -> Result<T, PayloadConversionError> {
63
+ let context = SerializationContext {
64
+ data,
65
+ converter: &self.payload_converter,
66
+ };
67
+ let decoded = self.codec.decode(data, vec![payload]).await;
68
+ let payload = decoded
69
+ .into_iter()
70
+ .next()
71
+ .ok_or(PayloadConversionError::WrongEncoding)?;
72
+ self.payload_converter.from_payload(&context, payload)
73
+ }
74
+
75
+ /// Serialize a value into multiple payloads (e.g. for multi-arg support), applying the codec.
76
+ pub async fn to_payloads<T: TemporalSerializable + 'static>(
77
+ &self,
78
+ data: &SerializationContextData,
79
+ val: &T,
80
+ ) -> Result<Vec<Payload>, PayloadConversionError> {
81
+ let context = SerializationContext {
82
+ data,
83
+ converter: &self.payload_converter,
84
+ };
85
+ let payloads = self.payload_converter.to_payloads(&context, val)?;
86
+ Ok(self.codec.encode(data, payloads).await)
87
+ }
88
+
89
+ /// Deserialize a value from multiple payloads (e.g. for multi-arg support), applying the codec.
90
+ pub async fn from_payloads<T: TemporalDeserializable + 'static>(
91
+ &self,
92
+ data: &SerializationContextData,
93
+ payloads: Vec<Payload>,
94
+ ) -> Result<T, PayloadConversionError> {
95
+ let context = SerializationContext {
96
+ data,
97
+ converter: &self.payload_converter,
98
+ };
99
+ let decoded = self.codec.decode(data, payloads).await;
100
+ self.payload_converter.from_payloads(&context, decoded)
101
+ }
102
+
103
+ /// Returns the payload converter component of this data converter.
104
+ pub fn payload_converter(&self) -> &PayloadConverter {
105
+ &self.payload_converter
106
+ }
107
+
108
+ /// Returns the codec component of this data converter.
109
+ pub fn codec(&self) -> &(dyn PayloadCodec + Send + Sync) {
110
+ self.codec.as_ref()
111
+ }
112
+ }
113
+
114
+ /// Data about the serialization context, indicating where the serialization is occurring.
115
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
116
+ pub enum SerializationContextData {
117
+ /// Serialization is occurring in a workflow context.
118
+ Workflow,
119
+ /// Serialization is occurring in an activity context.
120
+ Activity,
121
+ /// Serialization is occurring in a nexus context.
122
+ Nexus,
123
+ /// No specific serialization context.
124
+ None,
125
+ }
126
+
127
+ /// Context for serialization operations, including the kind of context and the
128
+ /// payload converter for nested serialization.
129
+ #[derive(Clone, Copy)]
130
+ pub struct SerializationContext<'a> {
131
+ /// The kind of serialization context (workflow, activity, etc.).
132
+ pub data: &'a SerializationContextData,
133
+ /// Allows nested types to serialize their contents using the same converter.
134
+ pub converter: &'a PayloadConverter,
135
+ }
136
+ /// Converts values to and from [`Payload`]s using different encoding strategies.
137
+ #[derive(Clone)]
138
+ pub enum PayloadConverter {
139
+ /// Uses a serde-based converter for encoding/decoding.
140
+ Serde(Arc<dyn ErasedSerdePayloadConverter>),
141
+ /// This variant signals the user wants to delegate to wrapper types
142
+ UseWrappers,
143
+ /// Tries multiple converters in order until one succeeds.
144
+ Composite(Arc<CompositePayloadConverter>),
145
+ }
146
+
147
+ impl std::fmt::Debug for PayloadConverter {
148
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149
+ match self {
150
+ PayloadConverter::Serde(_) => write!(f, "PayloadConverter::Serde(...)"),
151
+ PayloadConverter::UseWrappers => write!(f, "PayloadConverter::UseWrappers"),
152
+ PayloadConverter::Composite(_) => write!(f, "PayloadConverter::Composite(...)"),
153
+ }
154
+ }
155
+ }
156
+ impl PayloadConverter {
157
+ /// Create a payload converter that uses JSON serialization via serde.
158
+ pub fn serde_json() -> Self {
159
+ Self::Serde(Arc::new(SerdeJsonPayloadConverter))
160
+ }
161
+ // TODO [rust-sdk-branch]: Proto binary, other standard built-ins
162
+ }
163
+
164
+ impl Default for PayloadConverter {
165
+ fn default() -> Self {
166
+ Self::Composite(Arc::new(CompositePayloadConverter {
167
+ converters: vec![Self::UseWrappers, Self::serde_json()],
168
+ }))
169
+ }
170
+ }
171
+
172
+ /// Errors that can occur during payload conversion.
173
+ #[derive(Debug)]
174
+ pub enum PayloadConversionError {
175
+ /// The payload's encoding does not match what the converter expects.
176
+ WrongEncoding,
177
+ /// An error occurred during encoding or decoding.
178
+ EncodingError(Box<dyn std::error::Error + Send + Sync>),
179
+ }
180
+
181
+ impl std::fmt::Display for PayloadConversionError {
182
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183
+ match self {
184
+ PayloadConversionError::WrongEncoding => write!(f, "Wrong encoding"),
185
+ PayloadConversionError::EncodingError(err) => write!(f, "Encoding error: {}", err),
186
+ }
187
+ }
188
+ }
189
+
190
+ impl std::error::Error for PayloadConversionError {
191
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
192
+ match self {
193
+ PayloadConversionError::WrongEncoding => None,
194
+ PayloadConversionError::EncodingError(err) => Some(err.as_ref()),
195
+ }
196
+ }
197
+ }
198
+
199
+ /// Converts between Rust errors and Temporal [`Failure`] protobufs.
200
+ pub trait FailureConverter {
201
+ /// Convert an error into a Temporal failure protobuf.
202
+ fn to_failure(
203
+ &self,
204
+ error: Box<dyn std::error::Error>,
205
+ payload_converter: &PayloadConverter,
206
+ context: &SerializationContextData,
207
+ ) -> Result<Failure, PayloadConversionError>;
208
+
209
+ /// Convert a Temporal failure protobuf back into a Rust error.
210
+ fn to_error(
211
+ &self,
212
+ failure: Failure,
213
+ payload_converter: &PayloadConverter,
214
+ context: &SerializationContextData,
215
+ ) -> Result<Box<dyn std::error::Error>, PayloadConversionError>;
216
+ }
217
+ /// Default (currently unimplemented) failure converter.
218
+ pub struct DefaultFailureConverter;
219
+ /// Encodes and decodes payloads, enabling encryption or compression.
220
+ pub trait PayloadCodec {
221
+ /// Encode payloads before they are sent to the server.
222
+ fn encode(
223
+ &self,
224
+ context: &SerializationContextData,
225
+ payloads: Vec<Payload>,
226
+ ) -> BoxFuture<'static, Vec<Payload>>;
227
+ /// Decode payloads after they are received from the server.
228
+ fn decode(
229
+ &self,
230
+ context: &SerializationContextData,
231
+ payloads: Vec<Payload>,
232
+ ) -> BoxFuture<'static, Vec<Payload>>;
233
+ }
234
+
235
+ impl<T: PayloadCodec> PayloadCodec for Arc<T> {
236
+ fn encode(
237
+ &self,
238
+ context: &SerializationContextData,
239
+ payloads: Vec<Payload>,
240
+ ) -> BoxFuture<'static, Vec<Payload>> {
241
+ (**self).encode(context, payloads)
242
+ }
243
+ fn decode(
244
+ &self,
245
+ context: &SerializationContextData,
246
+ payloads: Vec<Payload>,
247
+ ) -> BoxFuture<'static, Vec<Payload>> {
248
+ (**self).decode(context, payloads)
249
+ }
250
+ }
251
+
252
+ /// A no-op codec that passes payloads through unchanged.
253
+ pub struct DefaultPayloadCodec;
254
+
255
+ /// Indicates some type can be serialized for use with Temporal.
256
+ ///
257
+ /// You don't need to implement this unless you are using a non-serde-compatible custom converter,
258
+ /// in which case you should implement the to/from_payload functions on some wrapper type.
259
+ pub trait TemporalSerializable {
260
+ /// Return a reference to this value as a serde-serializable trait object.
261
+ fn as_serde(&self) -> Result<&dyn erased_serde::Serialize, PayloadConversionError> {
262
+ Err(PayloadConversionError::WrongEncoding)
263
+ }
264
+ /// Convert this value into a single [`Payload`].
265
+ fn to_payload(&self, _: &SerializationContext<'_>) -> Result<Payload, PayloadConversionError> {
266
+ Err(PayloadConversionError::WrongEncoding)
267
+ }
268
+ /// Convert to multiple payloads. Override this for types representing multiple arguments.
269
+ fn to_payloads(
270
+ &self,
271
+ ctx: &SerializationContext<'_>,
272
+ ) -> Result<Vec<Payload>, PayloadConversionError> {
273
+ Ok(vec![self.to_payload(ctx)?])
274
+ }
275
+ }
276
+
277
+ /// Indicates some type can be deserialized for use with Temporal.
278
+ ///
279
+ /// You don't need to implement this unless you are using a non-serde-compatible custom converter,
280
+ /// in which case you should implement the to/from_payload functions on some wrapper type.
281
+ pub trait TemporalDeserializable: Sized {
282
+ /// Deserialize from a serde-based payload converter.
283
+ fn from_serde(
284
+ _: &dyn ErasedSerdePayloadConverter,
285
+ _ctx: &SerializationContext<'_>,
286
+ _: Payload,
287
+ ) -> Result<Self, PayloadConversionError> {
288
+ Err(PayloadConversionError::WrongEncoding)
289
+ }
290
+ /// Deserialize from a single [`Payload`].
291
+ fn from_payload(
292
+ ctx: &SerializationContext<'_>,
293
+ payload: Payload,
294
+ ) -> Result<Self, PayloadConversionError> {
295
+ let _ = (ctx, payload);
296
+ Err(PayloadConversionError::WrongEncoding)
297
+ }
298
+ /// Convert from multiple payloads. Override this for types representing multiple arguments.
299
+ fn from_payloads(
300
+ ctx: &SerializationContext<'_>,
301
+ payloads: Vec<Payload>,
302
+ ) -> Result<Self, PayloadConversionError> {
303
+ if payloads.len() != 1 {
304
+ return Err(PayloadConversionError::WrongEncoding);
305
+ }
306
+ Self::from_payload(ctx, payloads.into_iter().next().unwrap())
307
+ }
308
+ }
309
+
310
+ /// An unconverted set of payloads, used when the caller wants to defer deserialization.
311
+ #[derive(Clone, Debug, Default)]
312
+ pub struct RawValue {
313
+ /// The underlying payloads.
314
+ pub payloads: Vec<Payload>,
315
+ }
316
+ impl RawValue {
317
+ /// A RawValue representing no meaningful data, containing a single default payload.
318
+ /// This ensures the value can still be serialized as a single payload.
319
+ pub fn empty() -> Self {
320
+ Self {
321
+ payloads: vec![Payload::default()],
322
+ }
323
+ }
324
+
325
+ /// Create a new RawValue from a vector of payloads.
326
+ pub fn new(payloads: Vec<Payload>) -> Self {
327
+ Self { payloads }
328
+ }
329
+
330
+ /// Create a [`RawValue`] by serializing a value with the given converter.
331
+ pub fn from_value<T: TemporalSerializable + 'static>(
332
+ value: &T,
333
+ converter: &PayloadConverter,
334
+ ) -> RawValue {
335
+ RawValue::new(vec![
336
+ converter
337
+ .to_payload(
338
+ &SerializationContext {
339
+ data: &SerializationContextData::None,
340
+ converter,
341
+ },
342
+ value,
343
+ )
344
+ .unwrap(),
345
+ ])
346
+ }
347
+
348
+ /// Deserialize this [`RawValue`] into a typed value using the given converter.
349
+ pub fn to_value<T: TemporalDeserializable + 'static>(self, converter: &PayloadConverter) -> T {
350
+ converter
351
+ .from_payload(
352
+ &SerializationContext {
353
+ data: &SerializationContextData::None,
354
+ converter,
355
+ },
356
+ self.payloads.into_iter().next().unwrap(),
357
+ )
358
+ .unwrap()
359
+ }
360
+ }
361
+
362
+ impl TemporalSerializable for RawValue {
363
+ fn to_payload(&self, _: &SerializationContext<'_>) -> Result<Payload, PayloadConversionError> {
364
+ Ok(self.payloads.first().cloned().unwrap_or_default())
365
+ }
366
+ fn to_payloads(
367
+ &self,
368
+ _: &SerializationContext<'_>,
369
+ ) -> Result<Vec<Payload>, PayloadConversionError> {
370
+ Ok(self.payloads.clone())
371
+ }
372
+ }
373
+
374
+ impl TemporalDeserializable for RawValue {
375
+ fn from_payload(
376
+ _: &SerializationContext<'_>,
377
+ p: Payload,
378
+ ) -> Result<Self, PayloadConversionError> {
379
+ Ok(RawValue { payloads: vec![p] })
380
+ }
381
+ fn from_payloads(
382
+ _: &SerializationContext<'_>,
383
+ payloads: Vec<Payload>,
384
+ ) -> Result<Self, PayloadConversionError> {
385
+ Ok(RawValue { payloads })
386
+ }
387
+ }
388
+
389
+ /// Generic interface for converting between typed values and [`Payload`]s.
390
+ pub trait GenericPayloadConverter {
391
+ /// Serialize a value into a single [`Payload`].
392
+ fn to_payload<T: TemporalSerializable + 'static>(
393
+ &self,
394
+ context: &SerializationContext<'_>,
395
+ val: &T,
396
+ ) -> Result<Payload, PayloadConversionError>;
397
+ /// Deserialize a value from a single [`Payload`].
398
+ #[allow(clippy::wrong_self_convention)]
399
+ fn from_payload<T: TemporalDeserializable + 'static>(
400
+ &self,
401
+ context: &SerializationContext<'_>,
402
+ payload: Payload,
403
+ ) -> Result<T, PayloadConversionError>;
404
+ /// Serialize a value into multiple [`Payload`]s.
405
+ fn to_payloads<T: TemporalSerializable + 'static>(
406
+ &self,
407
+ context: &SerializationContext<'_>,
408
+ val: &T,
409
+ ) -> Result<Vec<Payload>, PayloadConversionError> {
410
+ Ok(vec![self.to_payload(context, val)?])
411
+ }
412
+ /// Deserialize a value from multiple [`Payload`]s.
413
+ #[allow(clippy::wrong_self_convention)]
414
+ fn from_payloads<T: TemporalDeserializable + 'static>(
415
+ &self,
416
+ context: &SerializationContext<'_>,
417
+ payloads: Vec<Payload>,
418
+ ) -> Result<T, PayloadConversionError> {
419
+ if payloads.len() != 1 {
420
+ return Err(PayloadConversionError::WrongEncoding);
421
+ }
422
+ self.from_payload(context, payloads.into_iter().next().unwrap())
423
+ }
424
+ }
425
+
426
+ impl GenericPayloadConverter for PayloadConverter {
427
+ fn to_payload<T: TemporalSerializable + 'static>(
428
+ &self,
429
+ context: &SerializationContext<'_>,
430
+ val: &T,
431
+ ) -> Result<Payload, PayloadConversionError> {
432
+ let mut payloads = self.to_payloads(context, val)?;
433
+ if payloads.len() != 1 {
434
+ return Err(PayloadConversionError::WrongEncoding);
435
+ }
436
+ Ok(payloads.pop().unwrap())
437
+ }
438
+
439
+ fn from_payload<T: TemporalDeserializable + 'static>(
440
+ &self,
441
+ context: &SerializationContext<'_>,
442
+ payload: Payload,
443
+ ) -> Result<T, PayloadConversionError> {
444
+ self.from_payloads(context, vec![payload])
445
+ }
446
+
447
+ fn to_payloads<T: TemporalSerializable + 'static>(
448
+ &self,
449
+ context: &SerializationContext<'_>,
450
+ val: &T,
451
+ ) -> Result<Vec<Payload>, PayloadConversionError> {
452
+ match self {
453
+ PayloadConverter::Serde(pc) => Ok(vec![pc.to_payload(context.data, val.as_serde()?)?]),
454
+ PayloadConverter::UseWrappers => T::to_payloads(val, context),
455
+ PayloadConverter::Composite(composite) => {
456
+ for converter in &composite.converters {
457
+ match converter.to_payloads(context, val) {
458
+ Ok(payloads) => return Ok(payloads),
459
+ Err(PayloadConversionError::WrongEncoding) => continue,
460
+ Err(e) => return Err(e),
461
+ }
462
+ }
463
+ Err(PayloadConversionError::WrongEncoding)
464
+ }
465
+ }
466
+ }
467
+
468
+ fn from_payloads<T: TemporalDeserializable + 'static>(
469
+ &self,
470
+ context: &SerializationContext<'_>,
471
+ payloads: Vec<Payload>,
472
+ ) -> Result<T, PayloadConversionError> {
473
+ // Handle empty payloads as unit type ()
474
+ if payloads.is_empty() && std::any::TypeId::of::<T>() == std::any::TypeId::of::<()>() {
475
+ let boxed: Box<dyn std::any::Any> = Box::new(());
476
+ return Ok(*boxed.downcast::<T>().unwrap());
477
+ }
478
+
479
+ match self {
480
+ PayloadConverter::Serde(pc) => {
481
+ if payloads.len() != 1 {
482
+ return Err(PayloadConversionError::WrongEncoding);
483
+ }
484
+ T::from_serde(pc.as_ref(), context, payloads.into_iter().next().unwrap())
485
+ }
486
+ PayloadConverter::UseWrappers => T::from_payloads(context, payloads),
487
+ PayloadConverter::Composite(composite) => {
488
+ for converter in &composite.converters {
489
+ match converter.from_payloads(context, payloads.clone()) {
490
+ Ok(val) => return Ok(val),
491
+ Err(PayloadConversionError::WrongEncoding) => continue,
492
+ Err(e) => return Err(e),
493
+ }
494
+ }
495
+ Err(PayloadConversionError::WrongEncoding)
496
+ }
497
+ }
498
+ }
499
+ }
500
+
501
+ // TODO [rust-sdk-branch]: Potentially allow opt-out / no-serde compile flags
502
+ impl<T> TemporalSerializable for T
503
+ where
504
+ T: serde::Serialize,
505
+ {
506
+ fn as_serde(&self) -> Result<&dyn erased_serde::Serialize, PayloadConversionError> {
507
+ Ok(self)
508
+ }
509
+ }
510
+ impl<T> TemporalDeserializable for T
511
+ where
512
+ T: serde::de::DeserializeOwned,
513
+ {
514
+ fn from_serde(
515
+ pc: &dyn ErasedSerdePayloadConverter,
516
+ context: &SerializationContext<'_>,
517
+ payload: Payload,
518
+ ) -> Result<Self, PayloadConversionError>
519
+ where
520
+ Self: Sized,
521
+ {
522
+ let mut de = pc.from_payload(context.data, payload)?;
523
+ erased_serde::deserialize(&mut de)
524
+ .map_err(|e| PayloadConversionError::EncodingError(Box::new(e)))
525
+ }
526
+ }
527
+
528
+ struct SerdeJsonPayloadConverter;
529
+ impl ErasedSerdePayloadConverter for SerdeJsonPayloadConverter {
530
+ fn to_payload(
531
+ &self,
532
+ _: &SerializationContextData,
533
+ value: &dyn erased_serde::Serialize,
534
+ ) -> Result<Payload, PayloadConversionError> {
535
+ let as_json = serde_json::to_vec(value)
536
+ .map_err(|e| PayloadConversionError::EncodingError(e.into()))?;
537
+ Ok(Payload {
538
+ metadata: {
539
+ let mut hm = HashMap::new();
540
+ hm.insert("encoding".to_string(), b"json/plain".to_vec());
541
+ hm
542
+ },
543
+ data: as_json,
544
+ external_payloads: vec![],
545
+ })
546
+ }
547
+
548
+ fn from_payload(
549
+ &self,
550
+ _: &SerializationContextData,
551
+ payload: Payload,
552
+ ) -> Result<Box<dyn erased_serde::Deserializer<'static>>, PayloadConversionError> {
553
+ let encoding = payload.metadata.get("encoding").map(|v| v.as_slice());
554
+ if encoding != Some(b"json/plain".as_slice()) {
555
+ return Err(PayloadConversionError::WrongEncoding);
556
+ }
557
+ let json_v: serde_json::Value = serde_json::from_slice(&payload.data)
558
+ .map_err(|e| PayloadConversionError::EncodingError(Box::new(e)))?;
559
+ Ok(Box::new(<dyn erased_serde::Deserializer>::erase(json_v)))
560
+ }
561
+ }
562
+ /// Type-erased serde-based payload converter for use behind `dyn` trait objects.
563
+ pub trait ErasedSerdePayloadConverter: Send + Sync {
564
+ /// Serialize a type-erased serde value into a [`Payload`].
565
+ fn to_payload(
566
+ &self,
567
+ context: &SerializationContextData,
568
+ value: &dyn erased_serde::Serialize,
569
+ ) -> Result<Payload, PayloadConversionError>;
570
+ /// Deserialize a [`Payload`] into a type-erased serde deserializer.
571
+ #[allow(clippy::wrong_self_convention)]
572
+ fn from_payload(
573
+ &self,
574
+ context: &SerializationContextData,
575
+ payload: Payload,
576
+ ) -> Result<Box<dyn erased_serde::Deserializer<'static>>, PayloadConversionError>;
577
+ }
578
+
579
+ // TODO [rust-sdk-branch]: All prost things should be behind a compile flag
580
+
581
+ /// Wrapper for protobuf messages that implements [`TemporalSerializable`]/[`TemporalDeserializable`]
582
+ /// using `binary/protobuf` encoding.
583
+ pub struct ProstSerializable<T: prost::Message>(pub T);
584
+ impl<T> TemporalSerializable for ProstSerializable<T>
585
+ where
586
+ T: prost::Message + Default + 'static,
587
+ {
588
+ fn to_payload(&self, _: &SerializationContext<'_>) -> Result<Payload, PayloadConversionError> {
589
+ let as_proto = prost::Message::encode_to_vec(&self.0);
590
+ Ok(Payload {
591
+ metadata: {
592
+ let mut hm = HashMap::new();
593
+ hm.insert("encoding".to_string(), b"binary/protobuf".to_vec());
594
+ hm
595
+ },
596
+ data: as_proto,
597
+ external_payloads: vec![],
598
+ })
599
+ }
600
+ }
601
+ impl<T> TemporalDeserializable for ProstSerializable<T>
602
+ where
603
+ T: prost::Message + Default + 'static,
604
+ {
605
+ fn from_payload(
606
+ _: &SerializationContext<'_>,
607
+ p: Payload,
608
+ ) -> Result<Self, PayloadConversionError>
609
+ where
610
+ Self: Sized,
611
+ {
612
+ let encoding = p.metadata.get("encoding").map(|v| v.as_slice());
613
+ if encoding != Some(b"binary/protobuf".as_slice()) {
614
+ return Err(PayloadConversionError::WrongEncoding);
615
+ }
616
+ T::decode(p.data.as_slice())
617
+ .map(ProstSerializable)
618
+ .map_err(|e| PayloadConversionError::EncodingError(Box::new(e)))
619
+ }
620
+ }
621
+
622
+ /// A payload converter that delegates to an ordered list of inner converters.
623
+ #[derive(Clone)]
624
+ pub struct CompositePayloadConverter {
625
+ converters: Vec<PayloadConverter>,
626
+ }
627
+
628
+ impl Default for DataConverter {
629
+ fn default() -> Self {
630
+ Self::new(
631
+ PayloadConverter::default(),
632
+ DefaultFailureConverter,
633
+ DefaultPayloadCodec,
634
+ )
635
+ }
636
+ }
637
+ impl FailureConverter for DefaultFailureConverter {
638
+ fn to_failure(
639
+ &self,
640
+ _: Box<dyn std::error::Error>,
641
+ _: &PayloadConverter,
642
+ _: &SerializationContextData,
643
+ ) -> Result<Failure, PayloadConversionError> {
644
+ todo!()
645
+ }
646
+ fn to_error(
647
+ &self,
648
+ _: Failure,
649
+ _: &PayloadConverter,
650
+ _: &SerializationContextData,
651
+ ) -> Result<Box<dyn std::error::Error>, PayloadConversionError> {
652
+ todo!()
653
+ }
654
+ }
655
+ impl PayloadCodec for DefaultPayloadCodec {
656
+ fn encode(
657
+ &self,
658
+ _: &SerializationContextData,
659
+ payloads: Vec<Payload>,
660
+ ) -> BoxFuture<'static, Vec<Payload>> {
661
+ async move { payloads }.boxed()
662
+ }
663
+ fn decode(
664
+ &self,
665
+ _: &SerializationContextData,
666
+ payloads: Vec<Payload>,
667
+ ) -> BoxFuture<'static, Vec<Payload>> {
668
+ async move { payloads }.boxed()
669
+ }
670
+ }
671
+
672
+ /// Represents multiple arguments for workflows/activities that accept more than one argument.
673
+ /// Use this when interoperating with other language SDKs that allow multiple arguments.
674
+ macro_rules! impl_multi_args {
675
+ ($name:ident; $count:expr; $($idx:tt: $ty:ident),+) => {
676
+ #[doc = concat!("Wrapper for ", stringify!($count), " typed arguments, enabling multi-arg serialization.")]
677
+ #[derive(Clone, Debug, PartialEq, Eq)]
678
+ pub struct $name<$($ty),+>($(pub $ty),+);
679
+
680
+ impl<$($ty),+> TemporalSerializable for $name<$($ty),+>
681
+ where
682
+ $($ty: TemporalSerializable + 'static),+
683
+ {
684
+ fn to_payload(&self, _: &SerializationContext<'_>) -> Result<Payload, PayloadConversionError> {
685
+ Err(PayloadConversionError::WrongEncoding)
686
+ }
687
+ fn to_payloads(
688
+ &self,
689
+ ctx: &SerializationContext<'_>,
690
+ ) -> Result<Vec<Payload>, PayloadConversionError> {
691
+ Ok(vec![$(ctx.converter.to_payload(ctx, &self.$idx)?),+])
692
+ }
693
+ }
694
+
695
+ #[allow(non_snake_case)]
696
+ impl<$($ty),+> From<($($ty),+,)> for $name<$($ty),+> {
697
+ fn from(t: ($($ty),+,)) -> Self {
698
+ $name($(t.$idx),+)
699
+ }
700
+ }
701
+
702
+ impl<$($ty),+> TemporalDeserializable for $name<$($ty),+>
703
+ where
704
+ $($ty: TemporalDeserializable + 'static),+
705
+ {
706
+ fn from_payload(_: &SerializationContext<'_>, _: Payload) -> Result<Self, PayloadConversionError> {
707
+ Err(PayloadConversionError::WrongEncoding)
708
+ }
709
+ fn from_payloads(
710
+ ctx: &SerializationContext<'_>,
711
+ payloads: Vec<Payload>,
712
+ ) -> Result<Self, PayloadConversionError> {
713
+ if payloads.len() != $count {
714
+ return Err(PayloadConversionError::WrongEncoding);
715
+ }
716
+ let mut iter = payloads.into_iter();
717
+ Ok($name(
718
+ $(ctx.converter.from_payload::<$ty>(ctx, iter.next().unwrap())?),+
719
+ ))
720
+ }
721
+ }
722
+ };
723
+ }
724
+
725
+ impl_multi_args!(MultiArgs2; 2; 0: A, 1: B);
726
+ impl_multi_args!(MultiArgs3; 3; 0: A, 1: B, 2: C);
727
+ impl_multi_args!(MultiArgs4; 4; 0: A, 1: B, 2: C, 3: D);
728
+ impl_multi_args!(MultiArgs5; 5; 0: A, 1: B, 2: C, 3: D, 4: E);
729
+ impl_multi_args!(MultiArgs6; 6; 0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
730
+
731
+ #[cfg(test)]
732
+ mod tests {
733
+ use super::*;
734
+
735
+ #[test]
736
+ fn test_empty_payloads_as_unit_type() {
737
+ let converter = PayloadConverter::default();
738
+ let ctx = SerializationContext {
739
+ data: &SerializationContextData::Workflow,
740
+ converter: &converter,
741
+ };
742
+
743
+ let empty_payloads: Vec<Payload> = vec![];
744
+ let result: Result<(), _> = converter.from_payloads(&ctx, empty_payloads);
745
+
746
+ assert!(result.is_ok(), "Empty payloads should deserialize as ()");
747
+ }
748
+
749
+ #[test]
750
+ fn multi_args_round_trip() {
751
+ let converter = PayloadConverter::default();
752
+ let ctx = SerializationContext {
753
+ data: &SerializationContextData::Workflow,
754
+ converter: &converter,
755
+ };
756
+
757
+ let args = MultiArgs2("hello".to_string(), 42i32);
758
+ let payloads = converter.to_payloads(&ctx, &args).unwrap();
759
+ assert_eq!(payloads.len(), 2);
760
+
761
+ let result: MultiArgs2<String, i32> = converter.from_payloads(&ctx, payloads).unwrap();
762
+ assert_eq!(result, args);
763
+ }
764
+
765
+ #[test]
766
+ fn multi_args_from_tuple() {
767
+ let args: MultiArgs2<String, i32> = ("hello".to_string(), 42i32).into();
768
+ assert_eq!(args, MultiArgs2("hello".to_string(), 42));
769
+ }
770
+ }