restate_sdk/context/mod.rs
1//! Types exposing Restate functionalities to service handlers.
2
3use crate::endpoint::{ContextInternal, InputMetadata};
4use crate::errors::{HandlerResult, TerminalError};
5use crate::serde::{Deserialize, Serialize};
6use std::future::Future;
7use std::time::Duration;
8
9#[doc(hidden)]
10pub mod macro_support;
11mod map;
12mod request;
13mod run;
14mod select;
15mod select_any;
16
17pub use map::{MapDurableFuture, MapErrDurableFuture, MapOkDurableFuture};
18pub use request::{CallFuture, InvocationHandle, Request, RequestTarget, SendHandle, SignalHandle};
19pub use run::{RunClosure, RunFuture, RunRetryPolicy};
20pub use select_any::DurableFuturesUnordered;
21
22pub type HeaderMap = http::HeaderMap<String>;
23
24/// Service handler context.
25pub struct Context<'ctx> {
26 invocation_id: String,
27 random_seed: u64,
28 #[cfg(feature = "rand")]
29 std_rng: rand::prelude::StdRng,
30 headers: HeaderMap,
31 scope: Option<String>,
32 limit_key: Option<String>,
33 idempotency_key: Option<String>,
34 inner: &'ctx ContextInternal,
35}
36
37impl Context<'_> {
38 /// Get invocation id.
39 pub fn invocation_id(&self) -> &str {
40 &self.invocation_id
41 }
42
43 /// Get request headers.
44 pub fn headers(&self) -> &HeaderMap {
45 &self.headers
46 }
47
48 /// Get request headers.
49 pub fn headers_mut(&mut self) -> &HeaderMap {
50 &mut self.headers
51 }
52
53 /// Scope this invocation was submitted with, if any.
54 pub fn scope(&self) -> Option<&str> {
55 self.scope.as_deref()
56 }
57
58 /// Limit key this invocation was submitted with, if any.
59 pub fn limit_key(&self) -> Option<&str> {
60 self.limit_key.as_deref()
61 }
62
63 /// Idempotency key this invocation was submitted with, if any.
64 pub fn idempotency_key(&self) -> Option<&str> {
65 self.idempotency_key.as_deref()
66 }
67}
68
69impl<'ctx> From<(&'ctx ContextInternal, InputMetadata)> for Context<'ctx> {
70 fn from(value: (&'ctx ContextInternal, InputMetadata)) -> Self {
71 Self {
72 invocation_id: value.1.invocation_id,
73 random_seed: value.1.random_seed,
74 #[cfg(feature = "rand")]
75 std_rng: rand::prelude::SeedableRng::seed_from_u64(value.1.random_seed),
76 headers: value.1.headers,
77 scope: value.1.scope,
78 limit_key: value.1.limit_key,
79 idempotency_key: value.1.idempotency_key,
80 inner: value.0,
81 }
82 }
83}
84
85/// Object shared handler context.
86pub struct SharedObjectContext<'ctx> {
87 invocation_id: String,
88 key: String,
89 random_seed: u64,
90 #[cfg(feature = "rand")]
91 std_rng: rand::prelude::StdRng,
92 headers: HeaderMap,
93 scope: Option<String>,
94 limit_key: Option<String>,
95 idempotency_key: Option<String>,
96 pub(crate) inner: &'ctx ContextInternal,
97}
98
99impl SharedObjectContext<'_> {
100 /// Get invocation id.
101 pub fn invocation_id(&self) -> &str {
102 &self.invocation_id
103 }
104
105 /// Get object key.
106 pub fn key(&self) -> &str {
107 &self.key
108 }
109
110 /// Get request headers.
111 pub fn headers(&self) -> &HeaderMap {
112 &self.headers
113 }
114
115 /// Get request headers.
116 pub fn headers_mut(&mut self) -> &HeaderMap {
117 &mut self.headers
118 }
119
120 /// Scope this invocation was submitted with, if any.
121 pub fn scope(&self) -> Option<&str> {
122 self.scope.as_deref()
123 }
124
125 /// Limit key this invocation was submitted with, if any.
126 pub fn limit_key(&self) -> Option<&str> {
127 self.limit_key.as_deref()
128 }
129
130 /// Idempotency key this invocation was submitted with, if any.
131 pub fn idempotency_key(&self) -> Option<&str> {
132 self.idempotency_key.as_deref()
133 }
134}
135
136impl<'ctx> From<(&'ctx ContextInternal, InputMetadata)> for SharedObjectContext<'ctx> {
137 fn from(value: (&'ctx ContextInternal, InputMetadata)) -> Self {
138 Self {
139 invocation_id: value.1.invocation_id,
140 key: value.1.key,
141 random_seed: value.1.random_seed,
142 #[cfg(feature = "rand")]
143 std_rng: rand::prelude::SeedableRng::seed_from_u64(value.1.random_seed),
144 headers: value.1.headers,
145 scope: value.1.scope,
146 limit_key: value.1.limit_key,
147 idempotency_key: value.1.idempotency_key,
148 inner: value.0,
149 }
150 }
151}
152
153/// Object handler context.
154pub struct ObjectContext<'ctx> {
155 invocation_id: String,
156 key: String,
157 random_seed: u64,
158 #[cfg(feature = "rand")]
159 std_rng: rand::prelude::StdRng,
160 headers: HeaderMap,
161 scope: Option<String>,
162 limit_key: Option<String>,
163 idempotency_key: Option<String>,
164 pub(crate) inner: &'ctx ContextInternal,
165}
166
167impl ObjectContext<'_> {
168 /// Get invocation id.
169 pub fn invocation_id(&self) -> &str {
170 &self.invocation_id
171 }
172
173 /// Get object key.
174 pub fn key(&self) -> &str {
175 &self.key
176 }
177
178 /// Get request headers.
179 pub fn headers(&self) -> &HeaderMap {
180 &self.headers
181 }
182
183 /// Get request headers.
184 pub fn headers_mut(&mut self) -> &HeaderMap {
185 &mut self.headers
186 }
187
188 /// Scope this invocation was submitted with, if any.
189 pub fn scope(&self) -> Option<&str> {
190 self.scope.as_deref()
191 }
192
193 /// Limit key this invocation was submitted with, if any.
194 pub fn limit_key(&self) -> Option<&str> {
195 self.limit_key.as_deref()
196 }
197
198 /// Idempotency key this invocation was submitted with, if any.
199 pub fn idempotency_key(&self) -> Option<&str> {
200 self.idempotency_key.as_deref()
201 }
202}
203
204impl<'ctx> From<(&'ctx ContextInternal, InputMetadata)> for ObjectContext<'ctx> {
205 fn from(value: (&'ctx ContextInternal, InputMetadata)) -> Self {
206 Self {
207 invocation_id: value.1.invocation_id,
208 key: value.1.key,
209 random_seed: value.1.random_seed,
210 #[cfg(feature = "rand")]
211 std_rng: rand::prelude::SeedableRng::seed_from_u64(value.1.random_seed),
212 headers: value.1.headers,
213 scope: value.1.scope,
214 limit_key: value.1.limit_key,
215 idempotency_key: value.1.idempotency_key,
216 inner: value.0,
217 }
218 }
219}
220
221/// Workflow shared handler context.
222pub struct SharedWorkflowContext<'ctx> {
223 invocation_id: String,
224 key: String,
225 random_seed: u64,
226 #[cfg(feature = "rand")]
227 std_rng: rand::prelude::StdRng,
228 headers: HeaderMap,
229 scope: Option<String>,
230 limit_key: Option<String>,
231 idempotency_key: Option<String>,
232 pub(crate) inner: &'ctx ContextInternal,
233}
234
235impl<'ctx> From<(&'ctx ContextInternal, InputMetadata)> for SharedWorkflowContext<'ctx> {
236 fn from(value: (&'ctx ContextInternal, InputMetadata)) -> Self {
237 Self {
238 invocation_id: value.1.invocation_id,
239 key: value.1.key,
240 random_seed: value.1.random_seed,
241 #[cfg(feature = "rand")]
242 std_rng: rand::prelude::SeedableRng::seed_from_u64(value.1.random_seed),
243 headers: value.1.headers,
244 scope: value.1.scope,
245 limit_key: value.1.limit_key,
246 idempotency_key: value.1.idempotency_key,
247 inner: value.0,
248 }
249 }
250}
251
252impl SharedWorkflowContext<'_> {
253 /// Get invocation id.
254 pub fn invocation_id(&self) -> &str {
255 &self.invocation_id
256 }
257
258 /// Get workflow key.
259 pub fn key(&self) -> &str {
260 &self.key
261 }
262
263 /// Get request headers.
264 pub fn headers(&self) -> &HeaderMap {
265 &self.headers
266 }
267
268 /// Get request headers.
269 pub fn headers_mut(&mut self) -> &HeaderMap {
270 &mut self.headers
271 }
272
273 /// Scope this invocation was submitted with, if any.
274 pub fn scope(&self) -> Option<&str> {
275 self.scope.as_deref()
276 }
277
278 /// Limit key this invocation was submitted with, if any.
279 pub fn limit_key(&self) -> Option<&str> {
280 self.limit_key.as_deref()
281 }
282
283 /// Idempotency key this invocation was submitted with, if any.
284 pub fn idempotency_key(&self) -> Option<&str> {
285 self.idempotency_key.as_deref()
286 }
287}
288
289/// Workflow handler context.
290pub struct WorkflowContext<'ctx> {
291 invocation_id: String,
292 key: String,
293 random_seed: u64,
294 #[cfg(feature = "rand")]
295 std_rng: rand::prelude::StdRng,
296 headers: HeaderMap,
297 scope: Option<String>,
298 limit_key: Option<String>,
299 idempotency_key: Option<String>,
300 pub(crate) inner: &'ctx ContextInternal,
301}
302
303impl<'ctx> From<(&'ctx ContextInternal, InputMetadata)> for WorkflowContext<'ctx> {
304 fn from(value: (&'ctx ContextInternal, InputMetadata)) -> Self {
305 Self {
306 invocation_id: value.1.invocation_id,
307 key: value.1.key,
308 random_seed: value.1.random_seed,
309 #[cfg(feature = "rand")]
310 std_rng: rand::prelude::SeedableRng::seed_from_u64(value.1.random_seed),
311 headers: value.1.headers,
312 scope: value.1.scope,
313 limit_key: value.1.limit_key,
314 idempotency_key: value.1.idempotency_key,
315 inner: value.0,
316 }
317 }
318}
319
320impl WorkflowContext<'_> {
321 /// Get invocation id.
322 pub fn invocation_id(&self) -> &str {
323 &self.invocation_id
324 }
325
326 /// Get workflow key.
327 pub fn key(&self) -> &str {
328 &self.key
329 }
330
331 /// Get request headers.
332 pub fn headers(&self) -> &HeaderMap {
333 &self.headers
334 }
335
336 /// Get request headers.
337 pub fn headers_mut(&mut self) -> &HeaderMap {
338 &mut self.headers
339 }
340
341 /// Scope this invocation was submitted with, if any.
342 pub fn scope(&self) -> Option<&str> {
343 self.scope.as_deref()
344 }
345
346 /// Limit key this invocation was submitted with, if any.
347 pub fn limit_key(&self) -> Option<&str> {
348 self.limit_key.as_deref()
349 }
350
351 /// Idempotency key this invocation was submitted with, if any.
352 pub fn idempotency_key(&self) -> Option<&str> {
353 self.idempotency_key.as_deref()
354 }
355}
356
357///
358/// # Scheduling & Timers
359/// The Restate SDK includes durable timers.
360/// You can use these to let handlers sleep for a specified time, or to schedule a handler to be called at a later time.
361/// These timers are resilient to failures and restarts.
362/// Restate stores and keeps track of the timers and triggers them on time, even across failures and restarts.
363///
364/// ## Scheduling Async Tasks
365///
366/// To schedule a handler to be called at a later time, have a look at the documentation on [delayed calls](Request::send_after).
367///
368/// ## Durable sleep
369/// To sleep in a Restate application for ten seconds, do the following:
370///
371/// ```rust,no_run
372/// # use restate_sdk::prelude::*;
373/// # use std::convert::Infallible;
374/// # use std::time::Duration;
375/// #
376/// # async fn handle(ctx: Context<'_>) -> Result<(), HandlerError> {
377/// ctx.sleep(Duration::from_secs(10)).await?;
378/// # Ok(())
379/// # }
380/// ```
381///
382/// **Cost savings on FaaS**:
383/// Restate suspends the handler while it is sleeping, to free up resources.
384/// This is beneficial for AWS Lambda deployments, since you don't pay for the time the handler is sleeping.
385///
386/// **Sleeping in Virtual Objects**:
387/// Virtual Objects only process a single invocation at a time, so the Virtual Object will be blocked while sleeping.
388///
389/// <details>
390/// <summary>Clock synchronization Restate Server vs. SDK</summary>
391///
392/// The Restate SDK calculates the wake-up time based on the delay you specify.
393/// The Restate Server then uses this calculated time to wake up the handler.
394/// If the Restate Server and the SDK have different system clocks, the sleep duration might not be accurate.
395/// So make sure that the system clock of the Restate Server and the SDK have the same timezone and are synchronized.
396/// </details>
397pub trait ContextTimers<'ctx>: private::SealedContext<'ctx> {
398 /// Sleep using Restate
399 fn sleep(
400 &self,
401 duration: Duration,
402 ) -> impl DurableFuture<Output = Result<(), TerminalError>> + Send + 'ctx {
403 private::SealedContext::inner_context(self).sleep(duration)
404 }
405}
406
407impl<'ctx, CTX: private::SealedContext<'ctx>> ContextTimers<'ctx> for CTX {}
408
409/// # Service Communication
410///
411/// A handler can call another handler and wait for the response (request-response), or it can send a message without waiting for the response.
412///
413/// ## Request-response calls
414///
415/// Request-response calls are requests where the client waits for the response.
416///
417/// You can do request-response calls to Services, Virtual Objects, and Workflows, in the following way:
418///
419/// ```rust,no_run
420/// # #[path = "../../examples/services/mod.rs"]
421/// # mod services;
422/// # use services::my_virtual_object::MyVirtualObjectClient;
423/// # use services::my_service::MyServiceClient;
424/// # use services::my_workflow::MyWorkflowClient;
425/// # use restate_sdk::prelude::*;
426/// #
427/// # async fn greet(ctx: Context<'_>, greeting: String) -> Result<(), HandlerError> {
428/// // To a Service:
429/// let service_response = ctx
430/// .service_client::<MyServiceClient>()
431/// .my_handler(String::from("Hi!"))
432/// .call()
433/// .await?;
434///
435/// // To a Virtual Object:
436/// let object_response = ctx
437/// .object_client::<MyVirtualObjectClient>("Mary")
438/// .my_handler(String::from("Hi!"))
439/// .call()
440/// .await?;
441///
442/// // To a Workflow:
443/// let workflow_result = ctx
444/// .workflow_client::<MyWorkflowClient>("my-workflow-id")
445/// .run(String::from("Hi!"))
446/// .call()
447/// .await?;
448/// ctx.workflow_client::<MyWorkflowClient>("my-workflow-id")
449/// .interact_with_workflow()
450/// .call()
451/// .await?;
452/// # Ok(())
453/// # }
454/// ```
455///
456/// 1. **Create a service client**.
457/// - For Virtual Objects, you also need to supply the key of the Virtual Object you want to call, here `"Mary"`.
458/// - For Workflows, you need to supply a workflow ID that is unique per workflow execution.
459/// 2. **Specify the handler** you want to call and supply the request.
460/// 3. **Await** the call to retrieve the response.
461///
462/// **No need for manual retry logic**:
463/// Restate proxies all the calls and logs them in the journal.
464/// In case of failures, Restate takes care of retries, so you don't need to implement this yourself here.
465///
466/// ## Idempotency key, scope and concurrency limit key
467///
468/// Calls and sends can carry an idempotency key, a scope, and a concurrency limit key.
469/// A *scope* is a sub-grouping of resources that gets co-located by the restate-server (idempotency
470/// keys are scoped). A *limit key* enforces hierarchical concurrency limits within the scope, and can
471/// only be used together with a scope.
472///
473/// ```rust,no_run
474/// # #[path = "../../examples/services/mod.rs"]
475/// # mod services;
476/// # use services::my_service::MyServiceClient;
477/// # use restate_sdk::prelude::*;
478/// #
479/// # async fn greet(ctx: Context<'_>, greeting: String) -> Result<(), HandlerError> {
480/// ctx.service_client::<MyServiceClient>()
481/// .my_handler(String::from("Hi!"))
482/// .idempotency_key("req-1")
483/// .scope("tenant-123")
484/// .limit_key("api-key/user42")
485/// .call()
486/// .await?;
487/// # Ok(())
488/// # }
489/// ```
490///
491/// ## Sending messages
492///
493/// Handlers can send messages (a.k.a. one-way calls, or fire-and-forget calls), as follows:
494///
495/// ```rust,no_run
496/// # #[path = "../../examples/services/mod.rs"]
497/// # mod services;
498/// # use services::my_virtual_object::MyVirtualObjectClient;
499/// # use services::my_service::MyServiceClient;
500/// # use services::my_workflow::MyWorkflowClient;
501/// # use restate_sdk::prelude::*;
502/// #
503/// # async fn greet(ctx: Context<'_>, greeting: String) -> Result<(), HandlerError> {
504/// // To a Service:
505/// ctx.service_client::<MyServiceClient>()
506/// .my_handler(String::from("Hi!"))
507/// .send();
508///
509/// // To a Virtual Object:
510/// ctx.object_client::<MyVirtualObjectClient>("Mary")
511/// .my_handler(String::from("Hi!"))
512/// .send();
513///
514/// // To a Workflow:
515/// ctx.workflow_client::<MyWorkflowClient>("my-workflow-id")
516/// .run(String::from("Hi!"))
517/// .send();
518/// ctx.workflow_client::<MyWorkflowClient>("my-workflow-id")
519/// .interact_with_workflow()
520/// .send();
521/// # Ok(())
522/// # }
523/// ```
524///
525/// **No need for message queues**:
526/// Without Restate, you would usually put a message queue in between the two services, to guarantee the message delivery.
527/// Restate eliminates the need for a message queue because Restate durably logs the request and makes sure it gets executed.
528///
529/// ## Delayed calls
530///
531/// A delayed call is a one-way call that gets executed after a specified delay.
532///
533/// To schedule a delayed call, send a message with a delay parameter, as follows:
534///
535/// ```rust,no_run
536/// # #[path = "../../examples/services/mod.rs"]
537/// # mod services;
538/// # use services::my_virtual_object::MyVirtualObjectClient;
539/// # use services::my_service::MyServiceClient;
540/// # use services::my_workflow::MyWorkflowClient;
541/// # use restate_sdk::prelude::*;
542/// # use std::time::Duration;
543/// #
544/// # async fn greet(ctx: Context<'_>, greeting: String) -> Result<(), HandlerError> {
545/// // To a Service:
546/// ctx.service_client::<MyServiceClient>()
547/// .my_handler(String::from("Hi!"))
548/// .send_after(Duration::from_millis(5000));
549///
550/// // To a Virtual Object:
551/// ctx.object_client::<MyVirtualObjectClient>("Mary")
552/// .my_handler(String::from("Hi!"))
553/// .send_after(Duration::from_millis(5000));
554///
555/// // To a Workflow:
556/// ctx.workflow_client::<MyWorkflowClient>("my-workflow-id")
557/// .run(String::from("Hi!"))
558/// .send_after(Duration::from_millis(5000));
559/// ctx.workflow_client::<MyWorkflowClient>("my-workflow-id")
560/// .interact_with_workflow()
561/// .send_after(Duration::from_millis(5000));
562/// # Ok(())
563/// # }
564/// ```
565///
566/// You can also use this functionality to schedule async tasks.
567/// Restate will make sure the task gets executed at the desired time.
568///
569/// ### Ordering guarantees in Virtual Objects
570/// Invocations to a Virtual Object are executed serially.
571/// Invocations will execute in the same order in which they arrive at Restate.
572/// For example, assume a handler calls the same Virtual Object twice:
573///
574/// ```rust,no_run
575/// # #[path = "../../examples/services/my_virtual_object.rs"]
576/// # mod my_virtual_object;
577/// # use my_virtual_object::MyVirtualObjectClient;
578/// # use restate_sdk::prelude::*;
579/// # async fn greet(ctx: Context<'_>, greeting: String) -> Result<(), HandlerError> {
580/// ctx.object_client::<MyVirtualObjectClient>("Mary")
581/// .my_handler(String::from("I'm call A!"))
582/// .send();
583/// ctx.object_client::<MyVirtualObjectClient>("Mary")
584/// .my_handler(String::from("I'm call B!"))
585/// .send();
586/// # Ok(())
587/// }
588/// ```
589///
590/// It is guaranteed that call A will execute before call B.
591/// It is not guaranteed though that call B will be executed immediately after call A, as invocations coming from other handlers/sources, could interleave these two calls.
592///
593/// ### Deadlocks with Virtual Objects
594/// Request-response calls to Virtual Objects can lead to deadlocks, in which the Virtual Object remains locked and can't process any more requests.
595/// Some example cases:
596/// - Cross deadlock between Virtual Object A and B: A calls B, and B calls A, both using same keys.
597/// - Cyclical deadlock: A calls B, and B calls C, and C calls A again.
598///
599/// In this situation, you can use the CLI to unblock the Virtual Object manually by [cancelling invocations](https://docs.restate.dev/operate/invocation#cancelling-invocations).
600///
601pub trait ContextClient<'ctx>: private::SealedContext<'ctx> {
602 /// Create a [`Request`].
603 fn request<Req, Res>(
604 &self,
605 request_target: RequestTarget,
606 req: Req,
607 ) -> Request<'ctx, Req, Res> {
608 Request::new(self.inner_context(), request_target, req)
609 }
610
611 /// Create an [`InvocationHandle`] from an invocation id.
612 fn invocation_handle(&self, invocation_id: String) -> InvocationHandle {
613 self.inner_context().invocation_handle(invocation_id)
614 }
615
616 /// Create a service client. The service client is generated by the [`restate_sdk_macros::service`] macro with the same name of the trait suffixed with `Client`.
617 ///
618 /// ```rust,no_run
619 /// # use std::time::Duration;
620 /// # use restate_sdk::prelude::*;
621 ///
622 /// #[restate_sdk::service]
623 /// trait MyService {
624 /// async fn handle() -> HandlerResult<()>;
625 /// }
626 ///
627 /// # async fn handler(ctx: Context<'_>) {
628 /// let client = ctx.service_client::<MyServiceClient>();
629 ///
630 /// // Do request
631 /// let result = client.handle().call().await;
632 ///
633 /// // Just send the request, don't wait the response
634 /// client.handle().send();
635 ///
636 /// // Schedule the request to be executed later
637 /// client.handle().send_after(Duration::from_secs(60));
638 /// # }
639 /// ```
640 fn service_client<C>(&self) -> C
641 where
642 C: IntoServiceClient<'ctx>,
643 {
644 C::create_client(self.inner_context())
645 }
646
647 /// Create an object client. The object client is generated by the [`restate_sdk_macros::object`] macro with the same name of the trait suffixed with `Client`.
648 ///
649 /// ```rust,no_run
650 /// # use std::time::Duration;
651 /// # use restate_sdk::prelude::*;
652 ///
653 /// #[restate_sdk::object]
654 /// trait MyObject {
655 /// async fn handle() -> HandlerResult<()>;
656 /// }
657 ///
658 /// # async fn handler(ctx: Context<'_>) {
659 /// let client = ctx.object_client::<MyObjectClient>("my-key");
660 ///
661 /// // Do request
662 /// let result = client.handle().call().await;
663 ///
664 /// // Just send the request, don't wait the response
665 /// client.handle().send();
666 ///
667 /// // Schedule the request to be executed later
668 /// client.handle().send_after(Duration::from_secs(60));
669 /// # }
670 /// ```
671 fn object_client<C>(&self, key: impl Into<String>) -> C
672 where
673 C: IntoObjectClient<'ctx>,
674 {
675 C::create_client(self.inner_context(), key.into())
676 }
677
678 /// Create an workflow client. The workflow client is generated by the [`restate_sdk_macros::workflow`] macro with the same name of the trait suffixed with `Client`.
679 ///
680 /// ```rust,no_run
681 /// # use std::time::Duration;
682 /// # use restate_sdk::prelude::*;
683 ///
684 /// #[restate_sdk::workflow]
685 /// trait MyWorkflow {
686 /// async fn handle() -> HandlerResult<()>;
687 /// }
688 ///
689 /// # async fn handler(ctx: Context<'_>) {
690 /// let client = ctx.workflow_client::<MyWorkflowClient>("my-key");
691 ///
692 /// // Do request
693 /// let result = client.handle().call().await;
694 ///
695 /// // Just send the request, don't wait the response
696 /// client.handle().send();
697 ///
698 /// // Schedule the request to be executed later
699 /// client.handle().send_after(Duration::from_secs(60));
700 /// # }
701 /// ```
702 fn workflow_client<C>(&self, key: impl Into<String>) -> C
703 where
704 C: IntoWorkflowClient<'ctx>,
705 {
706 C::create_client(self.inner_context(), key.into())
707 }
708}
709
710/// Trait used by codegen to create the service client.
711pub trait IntoServiceClient<'ctx>: Sized {
712 fn create_client(ctx: &'ctx ContextInternal) -> Self;
713}
714
715/// Trait used by codegen to use the object client.
716pub trait IntoObjectClient<'ctx>: Sized {
717 fn create_client(ctx: &'ctx ContextInternal, key: String) -> Self;
718}
719
720/// Trait used by codegen to use the workflow client.
721pub trait IntoWorkflowClient<'ctx>: Sized {
722 fn create_client(ctx: &'ctx ContextInternal, key: String) -> Self;
723}
724
725impl<'ctx, CTX: private::SealedContext<'ctx>> ContextClient<'ctx> for CTX {}
726
727/// # Awakeables
728///
729/// Awakeables pause an invocation while waiting for another process to complete a task.
730/// You can use this pattern to let a handler execute a task somewhere else and retrieve the result.
731/// This pattern is also known as the callback (task token) pattern.
732///
733/// ## Creating awakeables
734///
735/// 1. **Create an awakeable**. This contains a String identifier and a Promise.
736/// 2. **Trigger a task/process** and attach the awakeable ID (e.g., over Kafka, via HTTP, ...).
737/// For example, send an HTTP request to a service that executes the task, and attach the ID to the payload.
738/// You use `ctx.run` to avoid re-triggering the task on retries.
739/// 3. **Wait** until the other process has executed the task.
740/// The handler **receives the payload and resumes**.
741///
742/// ```rust,no_run
743/// # use restate_sdk::prelude::*;
744/// #
745/// # async fn handle(ctx: Context<'_>) -> Result<(), HandlerError> {
746/// /// 1. Create an awakeable
747/// let (id, promise) = ctx.awakeable::<String>();
748///
749/// /// 2. Trigger a task
750/// ctx.run(|| trigger_task_and_deliver_id(id.clone())).await?;
751///
752/// /// 3. Wait for the promise to be resolved
753/// let payload = promise.await?;
754/// # Ok(())
755/// # }
756/// # async fn trigger_task_and_deliver_id(awakeable_id: String) -> Result<(), HandlerError>{
757/// # Ok(())
758/// # }
759/// ```
760///
761///
762/// ## Completing awakeables
763///
764/// The external process completes the awakeable by either resolving it with an optional payload or by rejecting it
765/// with its ID and a reason for the failure. This throws [a terminal error][crate::errors] in the waiting handler.
766///
767/// - Resolving over HTTP with its ID and an optional payload:
768///
769/// ```text
770/// curl localhost:8080/restate/awakeables/prom_1PePOqp/resolve
771/// -H 'content-type: application/json'
772/// -d '{"hello": "world"}'
773/// ```
774///
775/// - Rejecting over HTTP with its ID and a reason:
776///
777/// ```text
778/// curl localhost:8080/restate/awakeables/prom_1PePOqp/reject
779/// -H 'content-type: text/plain' \
780/// -d 'Very bad error!'
781/// ```
782///
783/// - Resolving via the SDK with its ID and an optional payload, or rejecting with its ID and a reason:
784///
785/// ```rust,no_run
786/// # use restate_sdk::prelude::*;
787/// #
788/// # async fn handle(ctx: Context<'_>, id: String) -> Result<(), HandlerError> {
789/// // Resolve the awakeable
790/// ctx.resolve_awakeable(&id, "hello".to_string());
791///
792/// // Or reject the awakeable
793/// ctx.reject_awakeable(&id, TerminalError::new("my error reason"));
794/// # Ok(())
795/// # }
796/// ```
797///
798/// For more info about serialization of the payload, see [crate::serde]
799///
800/// When running on Function-as-a-Service platforms, such as AWS Lambda, Restate suspends the handler while waiting for the awakeable to be completed.
801/// Since you only pay for the time that the handler is actually running, you don't pay while waiting for the external process to return.
802///
803/// **Be aware**: Virtual Objects only process a single invocation at a time, so the Virtual Object will be blocked while waiting on the awakeable to be resolved.
804pub trait ContextAwakeables<'ctx>: private::SealedContext<'ctx> {
805 /// Create an awakeable
806 fn awakeable<T: Deserialize + 'static>(
807 &self,
808 ) -> (
809 String,
810 impl DurableFuture<Output = Result<T, TerminalError>> + Send + 'ctx,
811 ) {
812 self.inner_context().awakeable()
813 }
814
815 /// Resolve an awakeable
816 fn resolve_awakeable<T: Serialize + 'static>(&self, key: &str, t: T) {
817 self.inner_context().resolve_awakeable(key, t)
818 }
819
820 /// Resolve an awakeable
821 fn reject_awakeable(&self, key: &str, failure: TerminalError) {
822 self.inner_context().reject_awakeable(key, failure)
823 }
824}
825
826impl<'ctx, CTX: private::SealedContext<'ctx>> ContextAwakeables<'ctx> for CTX {}
827
828/// # Signals
829///
830/// Signals let invocations communicate with each other. A signal is a named, one-shot
831/// durable promise **scoped to a specific invocation**: a running handler awaits a signal by name,
832/// and any other handler completes it by targeting the awaiter's **invocation id** and the same
833/// **signal name**.
834///
835/// Signals are similar to [awakeables][crate::context::ContextAwakeables], but instead of an
836/// opaque generated identifier you exfiltrate, they are addressed by `(invocation_id, name)`.
837/// The awaiting handler just needs to share its own invocation id (available via
838/// [`Context::invocation_id`]).
839///
840/// ## Awaiting a signal
841///
842/// ```rust,no_run
843/// # use restate_sdk::prelude::*;
844/// # async fn handle(ctx: Context<'_>) -> Result<(), HandlerError> {
845/// // Wait for a named signal on the current invocation
846/// let value = ctx.signal::<String>("my-signal").await?;
847/// # Ok(())
848/// # }
849/// ```
850///
851/// The returned future is a [`DurableFuture`], so it can be combined with other durable futures
852/// using [`crate::select`].
853///
854/// ## Completing a signal
855///
856/// Another handler completes the signal on the target invocation using its
857/// [`InvocationHandle`][crate::context::InvocationHandle]:
858///
859/// ```rust,no_run
860/// # use restate_sdk::prelude::*;
861/// // Resolve the signal with a value
862/// # async fn resolve(ctx: Context<'_>, invocation_id: String) -> Result<(), HandlerError> {
863/// ctx.invocation_handle(invocation_id)
864/// .signal("my-signal")
865/// .resolve("hello".to_string());
866/// # Ok(())
867/// # }
868///
869/// // Or reject it, so the awaiting handler observes a terminal error
870/// # async fn reject(ctx: Context<'_>, invocation_id: String) -> Result<(), HandlerError> {
871/// ctx.invocation_handle(invocation_id)
872/// .signal("my-signal")
873/// .reject(TerminalError::new("my error reason"));
874/// # Ok(())
875/// # }
876/// ```
877///
878/// For more info about serialization of the payload, see [crate::serde].
879///
880/// **Be aware**: Virtual Objects only process a single invocation at a time, so the Virtual Object
881/// will be blocked while waiting on the signal to be completed.
882pub trait ContextSignals<'ctx>: private::SealedContext<'ctx> {
883 /// Wait for a named signal to arrive on the current invocation.
884 ///
885 /// Signals are identified by name and are scoped to the current invocation. Another handler
886 /// can complete this signal via [`InvocationHandle::signal`][crate::context::InvocationHandle::signal],
887 /// specifying this invocation's id (available via [`Context::invocation_id`]) and the same name.
888 fn signal<T: Deserialize + 'static>(
889 &self,
890 name: &str,
891 ) -> impl DurableFuture<Output = Result<T, TerminalError>> + Send + 'ctx {
892 self.inner_context().signal(name)
893 }
894}
895
896impl<'ctx, CTX: private::SealedContext<'ctx>> ContextSignals<'ctx> for CTX {}
897
898/// # Journaling Results
899///
900/// Restate uses an execution log for replay after failures and suspensions.
901/// This means that non-deterministic results (e.g. database responses, UUID generation) need to be stored in the execution log.
902/// The SDK offers some functionalities to help you with this:
903/// 1. **[Journaled actions][crate::context::ContextSideEffects::run]**: Run any block of code and store the result in Restate. Restate replays the result instead of re-executing the block on retries.
904/// 2. **[UUID generator][crate::context::ContextSideEffects::rand_uuid]**: Built-in helpers for generating stable UUIDs. Restate seeds the random number generator with the invocation ID, so it always returns the same value on retries.
905/// 3. **[Random generator][crate::context::ContextSideEffects::rand]**: Built-in helpers for generating randoms. Restate seeds the random number generator with the invocation ID, so it always returns the same value on retries.
906///
907pub trait ContextSideEffects<'ctx>: private::SealedContext<'ctx> {
908 /// ## Journaled actions
909 /// You can store the result of a (non-deterministic) operation in the Restate execution log (e.g. database requests, HTTP calls, etc).
910 /// Restate replays the result instead of re-executing the operation on retries.
911 ///
912 /// Here is an example of a database request for which the string response is stored in Restate:
913 /// ```rust,no_run
914 /// # use restate_sdk::prelude::*;
915 /// # async fn handle(ctx: Context<'_>) -> Result<(), HandlerError> {
916 /// let response = ctx.run(|| do_db_request()).await?;
917 /// # Ok(())
918 /// # }
919 /// # async fn do_db_request() -> Result<String, HandlerError>{
920 /// # Ok("Hello".to_string())
921 /// # }
922 /// ```
923 ///
924 /// You cannot use the Restate context within `ctx.run`.
925 /// This includes actions such as getting state, calling another service, and nesting other journaled actions.
926 ///
927 /// For more info about serialization of the return values, see [crate::serde].
928 ///
929 /// You can configure the retry policy for the `ctx.run` block:
930 /// ```rust,no_run
931 /// # use std::time::Duration;
932 /// # use restate_sdk::prelude::*;
933 /// # async fn handle(ctx: Context<'_>) -> Result<(), HandlerError> {
934 /// let my_run_retry_policy = RunRetryPolicy::default()
935 /// .initial_delay(Duration::from_millis(100))
936 /// .exponentiation_factor(2.0)
937 /// .max_delay(Duration::from_millis(1000))
938 /// .max_attempts(10)
939 /// .max_duration(Duration::from_secs(10));
940 /// ctx.run(|| write_to_other_system())
941 /// .retry_policy(my_run_retry_policy)
942 /// .await
943 /// .map_err(|e| {
944 /// // Handle the terminal error after retries exhausted
945 /// // For example, undo previous actions (see sagas guide) and
946 /// // propagate the error back to the caller
947 /// e
948 /// })?;
949 /// # Ok(())
950 /// # }
951 /// # async fn write_to_other_system() -> Result<String, HandlerError>{
952 /// # Ok("Hello".to_string())
953 /// # }
954 /// ```
955 ///
956 /// This way you can override the default retry behavior of your Restate service for specific operations.
957 /// Have a look at [`RunFuture::retry_policy`] for more information.
958 ///
959 /// If you set a maximum number of attempts, then the `ctx.run` block will fail with a [TerminalError] once the retries are exhausted.
960 /// Have a look at the [Sagas guide](https://docs.restate.dev/guides/sagas) to learn how to undo previous actions of the handler to keep the system in a consistent state.
961 ///
962 /// **Caution: Immediately await journaled actions:**
963 /// Always immediately await `ctx.run`, before doing any other context calls.
964 /// If not, you might bump into non-determinism errors during replay,
965 /// because the journaled result can get interleaved with the other context calls in the journal in a non-deterministic way.
966 ///
967 #[must_use]
968 fn run<R, F, T>(&self, run_closure: R) -> impl RunFuture<Result<T, TerminalError>> + 'ctx
969 where
970 R: RunClosure<Fut = F, Output = T> + Send + 'ctx,
971 F: Future<Output = HandlerResult<T>> + Send + 'ctx,
972 T: Serialize + Deserialize + 'static,
973 {
974 self.inner_context().run(run_closure)
975 }
976
977 /// Return a random seed inherently predictable, based on the invocation id, which is not secret.
978 ///
979 /// This value is stable during the invocation lifecycle, thus across retries.
980 fn random_seed(&self) -> u64 {
981 private::SealedContext::random_seed(self)
982 }
983
984 /// ### Generating random numbers
985 ///
986 /// Return a [`rand::RngExt`] instance inherently predictable, seeded with [`ContextSideEffects::random_seed`].
987 ///
988 /// ```rust,no_run
989 /// # use restate_sdk::prelude::*;
990 /// async fn rand_generate(mut ctx: Context<'_>) {
991 /// let x: u32 = ctx.rand().random();
992 /// # }
993 /// ```
994 ///
995 /// This instance is useful to generate identifiers, idempotency keys, and for uniform sampling from a set of options.
996 /// If a cryptographically secure value is needed, please generate that externally using [`ContextSideEffects::run`].
997 #[cfg(feature = "rand")]
998 fn rand(&mut self) -> &mut rand::prelude::StdRng {
999 private::SealedContext::rand(self)
1000 }
1001 /// ### Generating UUIDs
1002 ///
1003 /// Returns a random [`uuid::Uuid`], generated using [`ContextSideEffects::rand`].
1004 ///
1005 /// You can use these UUIDs to generate stable idempotency keys, to deduplicate operations. For example, you can use this to let a payment service avoid duplicate payments during retries.
1006 ///
1007 /// Do not use this in cryptographic contexts.
1008 ///
1009 /// ```rust,no_run
1010 /// # use restate_sdk::prelude::*;
1011 /// # use uuid::Uuid;
1012 /// # async fn uuid_generate(mut ctx: Context<'_>) {
1013 /// let uuid: Uuid = ctx.rand_uuid();
1014 /// # }
1015 /// ```
1016 #[cfg(all(feature = "rand", feature = "uuid"))]
1017 fn rand_uuid(&mut self) -> uuid::Uuid {
1018 let rand = private::SealedContext::rand(self);
1019 uuid::Uuid::from_u64_pair(rand::Rng::next_u64(rand), rand::Rng::next_u64(rand))
1020 }
1021}
1022
1023impl<'ctx, CTX: private::SealedContext<'ctx>> ContextSideEffects<'ctx> for CTX {}
1024
1025/// # Reading state
1026/// You can store key-value state in Restate.
1027/// Restate makes sure the state is consistent with the processing of the code execution.
1028///
1029/// **This feature is only available for Virtual Objects and Workflows:**
1030/// - For **Virtual Objects**, the state is isolated per Virtual Object and lives forever (across invocations for that object).
1031/// - For **Workflows**, you can think of it as if every workflow execution is a new object. So the state is isolated to a single workflow execution. The state can only be mutated by the `run` handler of the workflow. The other handlers can only read the state.
1032///
1033/// ```rust,no_run
1034/// # use restate_sdk::prelude::*;
1035/// #
1036/// # async fn my_handler(ctx: ObjectContext<'_>) -> Result<(), HandlerError> {
1037/// /// 1. Listing state keys
1038/// /// List all the state keys that have entries in the state store for this object, via:
1039/// let keys = ctx.get_keys().await?;
1040///
1041/// /// 2. Getting a state value
1042/// let my_string = ctx
1043/// .get::<String>("my-key")
1044/// .await?
1045/// .unwrap_or(String::from("my-default"));
1046/// let my_number = ctx.get::<f64>("my-number-key").await?.unwrap_or_default();
1047///
1048/// /// 3. Setting a state value
1049/// ctx.set("my-key", String::from("my-value"));
1050///
1051/// /// 4. Clearing a state value
1052/// ctx.clear("my-key");
1053///
1054/// /// 5. Clearing all state values
1055/// /// Deletes all the state in Restate for the object
1056/// ctx.clear_all();
1057/// # Ok(())
1058/// # }
1059/// ```
1060///
1061/// For more info about serialization, see [crate::serde]
1062///
1063/// ### Command-line introspection
1064/// You can inspect and edit the K/V state stored in Restate via `psql` and the CLI.
1065/// Have a look at the [introspection docs](https://docs.restate.dev//operate/introspection#inspecting-application-state) for more information.
1066///
1067pub trait ContextReadState<'ctx>: private::SealedContext<'ctx> {
1068 /// Get state
1069 fn get<T: Deserialize + 'static>(
1070 &self,
1071 key: &'ctx str,
1072 ) -> impl Future<Output = Result<Option<T>, TerminalError>> + 'ctx {
1073 self.inner_context().get(key)
1074 }
1075
1076 /// Get state keys
1077 fn get_keys(&'ctx self) -> impl Future<Output = Result<Vec<String>, TerminalError>> + 'ctx {
1078 self.inner_context().get_keys()
1079 }
1080}
1081
1082impl<'ctx, CTX: private::SealedContext<'ctx> + private::SealedCanReadState> ContextReadState<'ctx>
1083 for CTX
1084{
1085}
1086
1087/// # Writing State
1088/// You can store key-value state in Restate.
1089/// Restate makes sure the state is consistent with the processing of the code execution.
1090///
1091/// **This feature is only available for Virtual Objects and Workflows:**
1092/// - For **Virtual Objects**, the state is isolated per Virtual Object and lives forever (across invocations for that object).
1093/// - For **Workflows**, you can think of it as if every workflow execution is a new object. So the state is isolated to a single workflow execution. The state can only be mutated by the `run` handler of the workflow. The other handlers can only read the state.
1094///
1095/// ```rust,no_run
1096/// # use restate_sdk::prelude::*;
1097/// #
1098/// # async fn my_handler(ctx: ObjectContext<'_>) -> Result<(), HandlerError> {
1099/// /// 1. Listing state keys
1100/// /// List all the state keys that have entries in the state store for this object, via:
1101/// let keys = ctx.get_keys().await?;
1102///
1103/// /// 2. Getting a state value
1104/// let my_string = ctx
1105/// .get::<String>("my-key")
1106/// .await?
1107/// .unwrap_or(String::from("my-default"));
1108/// let my_number = ctx.get::<f64>("my-number-key").await?.unwrap_or_default();
1109///
1110/// /// 3. Setting a state value
1111/// ctx.set("my-key", String::from("my-value"));
1112///
1113/// /// 4. Clearing a state value
1114/// ctx.clear("my-key");
1115///
1116/// /// 5. Clearing all state values
1117/// /// Deletes all the state in Restate for the object
1118/// ctx.clear_all();
1119/// # Ok(())
1120/// # }
1121/// ```
1122///
1123/// For more info about serialization, see [crate::serde]
1124///
1125/// ## Command-line introspection
1126/// You can inspect and edit the K/V state stored in Restate via `psql` and the CLI.
1127/// Have a look at the [introspection docs](https://docs.restate.dev//operate/introspection#inspecting-application-state) for more information.
1128///
1129pub trait ContextWriteState<'ctx>: private::SealedContext<'ctx> {
1130 /// Set state
1131 fn set<T: Serialize + 'static>(&self, key: &str, t: T) {
1132 self.inner_context().set(key, t)
1133 }
1134
1135 /// Clear state
1136 fn clear(&self, key: &str) {
1137 self.inner_context().clear(key)
1138 }
1139
1140 /// Clear all state
1141 fn clear_all(&self) {
1142 self.inner_context().clear_all()
1143 }
1144}
1145
1146impl<'ctx, CTX: private::SealedContext<'ctx> + private::SealedCanWriteState> ContextWriteState<'ctx>
1147 for CTX
1148{
1149}
1150
1151/// Trait exposing Restate promise functionalities.
1152///
1153/// A promise is a durable, distributed version of a Rust oneshot channel.
1154/// Restate keeps track of the promises across restarts/failures.
1155///
1156/// You can use this feature to implement interaction between different workflow handlers, e.g. to
1157/// send a signal from a shared handler to the workflow handler.
1158pub trait ContextPromises<'ctx>: private::SealedContext<'ctx> {
1159 /// Create a promise
1160 fn promise<T: Deserialize + 'static>(
1161 &'ctx self,
1162 key: &'ctx str,
1163 ) -> impl DurableFuture<Output = Result<T, TerminalError>> + 'ctx {
1164 self.inner_context().promise(key)
1165 }
1166
1167 /// Peek a promise
1168 fn peek_promise<T: Deserialize + 'static>(
1169 &self,
1170 key: &'ctx str,
1171 ) -> impl Future<Output = Result<Option<T>, TerminalError>> + 'ctx {
1172 self.inner_context().peek_promise(key)
1173 }
1174
1175 /// Resolve a promise
1176 fn resolve_promise<T: Serialize + 'static>(&self, key: &str, t: T) {
1177 self.inner_context().resolve_promise(key, t)
1178 }
1179
1180 /// Resolve a promise
1181 fn reject_promise(&self, key: &str, failure: TerminalError) {
1182 self.inner_context().reject_promise(key, failure)
1183 }
1184}
1185
1186impl<'ctx, CTX: private::SealedContext<'ctx> + private::SealedCanUsePromises> ContextPromises<'ctx>
1187 for CTX
1188{
1189}
1190
1191/// A future returned by a Restate operation, which can participate in combinators such as
1192/// [`select!`](crate::select) and [`DurableFuturesUnordered`].
1193///
1194/// The [`map`](DurableFuture::map)/[`map_ok`](DurableFuture::map_ok)/[`map_err`](DurableFuture::map_err)
1195/// combinators transform the output while keeping the result a [`DurableFuture`], so it can still be
1196/// used in combinators.
1197pub trait DurableFuture: Future + macro_support::SealedDurableFuture {
1198 /// Map the output of this durable future.
1199 fn map<U, M>(self, mapper: M) -> MapDurableFuture<Self, M>
1200 where
1201 Self: Sized,
1202 M: FnOnce(Self::Output) -> U,
1203 {
1204 MapDurableFuture::new(self, mapper)
1205 }
1206
1207 /// Map the success value of this durable future, leaving terminal errors untouched.
1208 fn map_ok<T, U, M>(self, mapper: M) -> MapOkDurableFuture<Self, M>
1209 where
1210 Self: Sized + Future<Output = Result<T, TerminalError>>,
1211 M: FnOnce(T) -> U,
1212 {
1213 MapOkDurableFuture::new(self, mapper)
1214 }
1215
1216 /// Map the terminal error of this durable future, leaving success values untouched.
1217 fn map_err<T, M>(self, mapper: M) -> MapErrDurableFuture<Self, M>
1218 where
1219 Self: Sized + Future<Output = Result<T, TerminalError>>,
1220 M: FnOnce(TerminalError) -> TerminalError,
1221 {
1222 MapErrDurableFuture::new(self, mapper)
1223 }
1224}
1225
1226impl<'a, O> macro_support::SealedDurableFuture
1227 for std::pin::Pin<Box<dyn DurableFuture<Output = O> + Send + 'a>>
1228{
1229 fn inner_context(&self) -> ContextInternal {
1230 (**self).inner_context()
1231 }
1232
1233 fn handle(&self) -> restate_sdk_shared_core::NotificationHandle {
1234 (**self).handle()
1235 }
1236}
1237
1238impl<'a, O> DurableFuture for std::pin::Pin<Box<dyn DurableFuture<Output = O> + Send + 'a>> {}
1239
1240pub(crate) mod private {
1241 use super::*;
1242 pub trait SealedContext<'ctx> {
1243 fn inner_context(&self) -> &'ctx ContextInternal;
1244
1245 fn random_seed(&self) -> u64;
1246
1247 #[cfg(feature = "rand")]
1248 fn rand(&mut self) -> &mut rand::prelude::StdRng;
1249 }
1250
1251 // Context capabilities
1252 pub trait SealedCanReadState {}
1253 pub trait SealedCanWriteState {}
1254 pub trait SealedCanUsePromises {}
1255
1256 impl<'ctx> SealedContext<'ctx> for Context<'ctx> {
1257 fn inner_context(&self) -> &'ctx ContextInternal {
1258 self.inner
1259 }
1260
1261 fn random_seed(&self) -> u64 {
1262 self.random_seed
1263 }
1264
1265 #[cfg(feature = "rand")]
1266 fn rand(&mut self) -> &mut rand::prelude::StdRng {
1267 &mut self.std_rng
1268 }
1269 }
1270
1271 impl<'ctx> SealedContext<'ctx> for SharedObjectContext<'ctx> {
1272 fn inner_context(&self) -> &'ctx ContextInternal {
1273 self.inner
1274 }
1275
1276 fn random_seed(&self) -> u64 {
1277 self.random_seed
1278 }
1279
1280 #[cfg(feature = "rand")]
1281 fn rand(&mut self) -> &mut rand::prelude::StdRng {
1282 &mut self.std_rng
1283 }
1284 }
1285
1286 impl SealedCanReadState for SharedObjectContext<'_> {}
1287
1288 impl<'ctx> SealedContext<'ctx> for ObjectContext<'ctx> {
1289 fn inner_context(&self) -> &'ctx ContextInternal {
1290 self.inner
1291 }
1292
1293 fn random_seed(&self) -> u64 {
1294 self.random_seed
1295 }
1296
1297 #[cfg(feature = "rand")]
1298 fn rand(&mut self) -> &mut rand::prelude::StdRng {
1299 &mut self.std_rng
1300 }
1301 }
1302
1303 impl SealedCanReadState for ObjectContext<'_> {}
1304 impl SealedCanWriteState for ObjectContext<'_> {}
1305
1306 impl<'ctx> SealedContext<'ctx> for SharedWorkflowContext<'ctx> {
1307 fn inner_context(&self) -> &'ctx ContextInternal {
1308 self.inner
1309 }
1310
1311 fn random_seed(&self) -> u64 {
1312 self.random_seed
1313 }
1314
1315 #[cfg(feature = "rand")]
1316 fn rand(&mut self) -> &mut rand::prelude::StdRng {
1317 &mut self.std_rng
1318 }
1319 }
1320
1321 impl SealedCanReadState for SharedWorkflowContext<'_> {}
1322 impl SealedCanUsePromises for SharedWorkflowContext<'_> {}
1323
1324 impl<'ctx> SealedContext<'ctx> for WorkflowContext<'ctx> {
1325 fn inner_context(&self) -> &'ctx ContextInternal {
1326 self.inner
1327 }
1328
1329 fn random_seed(&self) -> u64 {
1330 self.random_seed
1331 }
1332
1333 #[cfg(feature = "rand")]
1334 fn rand(&mut self) -> &mut rand::prelude::StdRng {
1335 &mut self.std_rng
1336 }
1337 }
1338
1339 impl SealedCanReadState for WorkflowContext<'_> {}
1340 impl SealedCanWriteState for WorkflowContext<'_> {}
1341 impl SealedCanUsePromises for WorkflowContext<'_> {}
1342}