Skip to main content

durable_lambda_core/
ops_trait.rs

1//! Shared trait for all durable context types.
2//!
3//! [`DurableContextOps`] is the single interface satisfied by every context
4//! type in the SDK: [`DurableContext`](crate::context::DurableContext),
5//! `ClosureContext`, `TraitContext`, and `BuilderContext`.
6//!
7//! This trait exists for **static dispatch only** — never use it as `dyn
8//! DurableContextOps`. It enables generic handler functions that work with any
9//! context flavour without code duplication.
10
11use std::future::Future;
12
13use serde::de::DeserializeOwned;
14use serde::Serialize;
15
16use crate::context::DurableContext;
17use crate::error::DurableError;
18use crate::types::{
19    BatchResult, CallbackHandle, CallbackOptions, CompensationResult, ExecutionMode, MapOptions,
20    ParallelOptions, StepOptions,
21};
22
23/// Shared interface for all durable context types.
24///
25/// Every context type in the SDK implements this trait by delegating to the
26/// underlying [`DurableContext`] or its inherent methods. Use this trait as a
27/// generic bound when writing handler logic that should work across all context
28/// approaches (closure, trait, builder, or core directly).
29///
30/// # Static dispatch only
31///
32/// This trait uses native async (`-> impl Future<...>`) and is designed for
33/// static dispatch. Do **not** use `dyn DurableContextOps` — there is no
34/// object-safe version.
35///
36/// # Examples
37///
38/// ```no_run
39/// use durable_lambda_core::DurableContextOps;
40/// use durable_lambda_core::error::DurableError;
41///
42/// async fn process_order<C: DurableContextOps>(ctx: &mut C, order_id: u64) -> Result<(), DurableError> {
43///     let _result: Result<String, String> = ctx.step("validate", move || async move {
44///         Ok(format!("validated:{order_id}"))
45///     }).await?;
46///     ctx.log("order processed");
47///     Ok(())
48/// }
49/// ```
50pub trait DurableContextOps {
51    // -------------------------------------------------------------------------
52    // Async operation methods
53    // -------------------------------------------------------------------------
54
55    /// Execute a named step with checkpointing.
56    ///
57    /// See [`DurableContext::step`](crate::context::DurableContext) for full
58    /// documentation.
59    fn step<T, E, F, Fut>(
60        &mut self,
61        name: &str,
62        f: F,
63    ) -> impl Future<Output = Result<Result<T, E>, DurableError>> + Send
64    where
65        T: Serialize + DeserializeOwned + Send + 'static,
66        E: Serialize + DeserializeOwned + Send + 'static,
67        F: FnOnce() -> Fut + Send + 'static,
68        Fut: Future<Output = Result<T, E>> + Send + 'static;
69
70    /// Execute a named step with checkpointing and retry configuration.
71    ///
72    /// See [`DurableContext::step_with_options`](crate::context::DurableContext) for full
73    /// documentation.
74    fn step_with_options<T, E, F, Fut>(
75        &mut self,
76        name: &str,
77        options: StepOptions,
78        f: F,
79    ) -> impl Future<Output = Result<Result<T, E>, DurableError>> + Send
80    where
81        T: Serialize + DeserializeOwned + Send + 'static,
82        E: Serialize + DeserializeOwned + Send + 'static,
83        F: FnOnce() -> Fut + Send + 'static,
84        Fut: Future<Output = Result<T, E>> + Send + 'static;
85
86    /// Suspend execution for the specified duration.
87    ///
88    /// See [`DurableContext::wait`](crate::context::DurableContext) for full
89    /// documentation.
90    fn wait(
91        &mut self,
92        name: &str,
93        duration_secs: i32,
94    ) -> impl Future<Output = Result<(), DurableError>> + Send;
95
96    /// Register a callback and return a handle with the server-generated callback ID.
97    ///
98    /// See [`DurableContext::create_callback`](crate::context::DurableContext) for full
99    /// documentation.
100    fn create_callback(
101        &mut self,
102        name: &str,
103        options: CallbackOptions,
104    ) -> impl Future<Output = Result<CallbackHandle, DurableError>> + Send;
105
106    /// Durably invoke another Lambda function and return its result.
107    ///
108    /// See [`DurableContext::invoke`](crate::context::DurableContext) for full
109    /// documentation.
110    fn invoke<T, P>(
111        &mut self,
112        name: &str,
113        function_name: &str,
114        payload: &P,
115    ) -> impl Future<Output = Result<T, DurableError>> + Send
116    where
117        T: DeserializeOwned + Send,
118        P: Serialize + Sync;
119
120    /// Execute multiple branches concurrently and return their results.
121    ///
122    /// See [`DurableContext::parallel`](crate::context::DurableContext) for full
123    /// documentation.
124    fn parallel<T, F, Fut>(
125        &mut self,
126        name: &str,
127        branches: Vec<F>,
128        options: ParallelOptions,
129    ) -> impl Future<Output = Result<BatchResult<T>, DurableError>> + Send
130    where
131        T: Serialize + DeserializeOwned + Send + 'static,
132        F: FnOnce(DurableContext) -> Fut + Send + 'static,
133        Fut: Future<Output = Result<T, DurableError>> + Send + 'static;
134
135    /// Execute an isolated subflow with its own checkpoint namespace.
136    ///
137    /// See [`DurableContext::child_context`](crate::context::DurableContext) for full
138    /// documentation.
139    fn child_context<T, F, Fut>(
140        &mut self,
141        name: &str,
142        f: F,
143    ) -> impl Future<Output = Result<T, DurableError>> + Send
144    where
145        T: Serialize + DeserializeOwned + Send,
146        F: FnOnce(DurableContext) -> Fut + Send,
147        Fut: Future<Output = Result<T, DurableError>> + Send;
148
149    /// Process a collection of items in parallel and return their results.
150    ///
151    /// See [`DurableContext::map`](crate::context::DurableContext) for full
152    /// documentation.
153    fn map<T, I, F, Fut>(
154        &mut self,
155        name: &str,
156        items: Vec<I>,
157        options: MapOptions,
158        f: F,
159    ) -> impl Future<Output = Result<BatchResult<T>, DurableError>> + Send
160    where
161        T: Serialize + DeserializeOwned + Send + 'static,
162        I: Send + 'static,
163        F: FnOnce(I, DurableContext) -> Fut + Send + 'static + Clone,
164        Fut: Future<Output = Result<T, DurableError>> + Send + 'static;
165
166    // -------------------------------------------------------------------------
167    // Compensation (saga pattern) methods
168    // -------------------------------------------------------------------------
169
170    /// Execute a forward step and register a compensation closure on success.
171    ///
172    /// See [`DurableContext::step_with_compensation`](crate::context::DurableContext) for full
173    /// documentation.
174    fn step_with_compensation<T, E, F, Fut, G, GFut>(
175        &mut self,
176        name: &str,
177        forward_fn: F,
178        compensate_fn: G,
179    ) -> impl Future<Output = Result<Result<T, E>, DurableError>> + Send
180    where
181        T: Serialize + DeserializeOwned + Send + 'static,
182        E: Serialize + DeserializeOwned + Send + 'static,
183        F: FnOnce() -> Fut + Send + 'static,
184        Fut: Future<Output = Result<T, E>> + Send + 'static,
185        G: FnOnce(T) -> GFut + Send + 'static,
186        GFut: Future<Output = Result<(), DurableError>> + Send + 'static;
187
188    /// Execute a forward step (with options) and register a compensation closure on success.
189    ///
190    /// See [`DurableContext::step_with_compensation_opts`](crate::context::DurableContext) for full
191    /// documentation.
192    fn step_with_compensation_opts<T, E, F, Fut, G, GFut>(
193        &mut self,
194        name: &str,
195        options: StepOptions,
196        forward_fn: F,
197        compensate_fn: G,
198    ) -> impl Future<Output = Result<Result<T, E>, DurableError>> + Send
199    where
200        T: Serialize + DeserializeOwned + Send + 'static,
201        E: Serialize + DeserializeOwned + Send + 'static,
202        F: FnOnce() -> Fut + Send + 'static,
203        Fut: Future<Output = Result<T, E>> + Send + 'static,
204        G: FnOnce(T) -> GFut + Send + 'static,
205        GFut: Future<Output = Result<(), DurableError>> + Send + 'static;
206
207    /// Execute all registered compensations in reverse registration order.
208    ///
209    /// See [`DurableContext::run_compensations`](crate::context::DurableContext) for full
210    /// documentation.
211    fn run_compensations(
212        &mut self,
213    ) -> impl Future<Output = Result<CompensationResult, DurableError>> + Send;
214
215    // -------------------------------------------------------------------------
216    // Sync operation method
217    // -------------------------------------------------------------------------
218
219    /// Check the result of a previously created callback.
220    ///
221    /// See [`DurableContext::callback_result`](crate::context::DurableContext) for full
222    /// documentation.
223    fn callback_result<T: DeserializeOwned>(
224        &self,
225        handle: &CallbackHandle,
226    ) -> Result<T, DurableError>;
227
228    // -------------------------------------------------------------------------
229    // State query methods
230    // -------------------------------------------------------------------------
231
232    /// Return the current execution mode (Replaying or Executing).
233    fn execution_mode(&self) -> ExecutionMode;
234
235    /// Return whether the context is currently replaying from history.
236    fn is_replaying(&self) -> bool;
237
238    /// Return a reference to the durable execution ARN.
239    fn arn(&self) -> &str;
240
241    /// Return the current checkpoint token.
242    fn checkpoint_token(&self) -> &str;
243
244    // -------------------------------------------------------------------------
245    // Log methods
246    // -------------------------------------------------------------------------
247
248    /// Emit a replay-safe info-level log message.
249    fn log(&self, message: &str);
250
251    /// Emit a replay-safe info-level log message with structured data.
252    fn log_with_data(&self, message: &str, data: &serde_json::Value);
253
254    /// Emit a replay-safe debug-level log message.
255    fn log_debug(&self, message: &str);
256
257    /// Emit a replay-safe warn-level log message.
258    fn log_warn(&self, message: &str);
259
260    /// Emit a replay-safe error-level log message.
261    fn log_error(&self, message: &str);
262
263    /// Emit a replay-safe debug-level log message with structured data.
264    fn log_debug_with_data(&self, message: &str, data: &serde_json::Value);
265
266    /// Emit a replay-safe warn-level log message with structured data.
267    fn log_warn_with_data(&self, message: &str, data: &serde_json::Value);
268
269    /// Emit a replay-safe error-level log message with structured data.
270    fn log_error_with_data(&self, message: &str, data: &serde_json::Value);
271
272    // -------------------------------------------------------------------------
273    // Batch checkpoint methods
274    // -------------------------------------------------------------------------
275
276    /// Enable batch checkpoint mode.
277    ///
278    /// See [`DurableContext::enable_batch_mode`](crate::context::DurableContext) for full
279    /// documentation.
280    fn enable_batch_mode(&mut self);
281
282    /// Flush accumulated batch checkpoint updates.
283    ///
284    /// See [`DurableContext::flush_batch`](crate::context::DurableContext) for full
285    /// documentation.
286    fn flush_batch(&mut self) -> impl Future<Output = Result<(), DurableError>> + Send;
287}
288
289impl DurableContextOps for DurableContext {
290    fn step<T, E, F, Fut>(
291        &mut self,
292        name: &str,
293        f: F,
294    ) -> impl Future<Output = Result<Result<T, E>, DurableError>> + Send
295    where
296        T: Serialize + DeserializeOwned + Send + 'static,
297        E: Serialize + DeserializeOwned + Send + 'static,
298        F: FnOnce() -> Fut + Send + 'static,
299        Fut: Future<Output = Result<T, E>> + Send + 'static,
300    {
301        DurableContext::step(self, name, f)
302    }
303
304    fn step_with_options<T, E, F, Fut>(
305        &mut self,
306        name: &str,
307        options: StepOptions,
308        f: F,
309    ) -> impl Future<Output = Result<Result<T, E>, DurableError>> + Send
310    where
311        T: Serialize + DeserializeOwned + Send + 'static,
312        E: Serialize + DeserializeOwned + Send + 'static,
313        F: FnOnce() -> Fut + Send + 'static,
314        Fut: Future<Output = Result<T, E>> + Send + 'static,
315    {
316        DurableContext::step_with_options(self, name, options, f)
317    }
318
319    fn wait(
320        &mut self,
321        name: &str,
322        duration_secs: i32,
323    ) -> impl Future<Output = Result<(), DurableError>> + Send {
324        DurableContext::wait(self, name, duration_secs)
325    }
326
327    fn create_callback(
328        &mut self,
329        name: &str,
330        options: CallbackOptions,
331    ) -> impl Future<Output = Result<CallbackHandle, DurableError>> + Send {
332        DurableContext::create_callback(self, name, options)
333    }
334
335    fn invoke<T, P>(
336        &mut self,
337        name: &str,
338        function_name: &str,
339        payload: &P,
340    ) -> impl Future<Output = Result<T, DurableError>> + Send
341    where
342        T: DeserializeOwned + Send,
343        P: Serialize + Sync,
344    {
345        DurableContext::invoke(self, name, function_name, payload)
346    }
347
348    fn parallel<T, F, Fut>(
349        &mut self,
350        name: &str,
351        branches: Vec<F>,
352        options: ParallelOptions,
353    ) -> impl Future<Output = Result<BatchResult<T>, DurableError>> + Send
354    where
355        T: Serialize + DeserializeOwned + Send + 'static,
356        F: FnOnce(DurableContext) -> Fut + Send + 'static,
357        Fut: Future<Output = Result<T, DurableError>> + Send + 'static,
358    {
359        DurableContext::parallel(self, name, branches, options)
360    }
361
362    fn child_context<T, F, Fut>(
363        &mut self,
364        name: &str,
365        f: F,
366    ) -> impl Future<Output = Result<T, DurableError>> + Send
367    where
368        T: Serialize + DeserializeOwned + Send,
369        F: FnOnce(DurableContext) -> Fut + Send,
370        Fut: Future<Output = Result<T, DurableError>> + Send,
371    {
372        DurableContext::child_context(self, name, f)
373    }
374
375    fn map<T, I, F, Fut>(
376        &mut self,
377        name: &str,
378        items: Vec<I>,
379        options: MapOptions,
380        f: F,
381    ) -> impl Future<Output = Result<BatchResult<T>, DurableError>> + Send
382    where
383        T: Serialize + DeserializeOwned + Send + 'static,
384        I: Send + 'static,
385        F: FnOnce(I, DurableContext) -> Fut + Send + 'static + Clone,
386        Fut: Future<Output = Result<T, DurableError>> + Send + 'static,
387    {
388        DurableContext::map(self, name, items, options, f)
389    }
390
391    fn step_with_compensation<T, E, F, Fut, G, GFut>(
392        &mut self,
393        name: &str,
394        forward_fn: F,
395        compensate_fn: G,
396    ) -> impl Future<Output = Result<Result<T, E>, DurableError>> + Send
397    where
398        T: Serialize + DeserializeOwned + Send + 'static,
399        E: Serialize + DeserializeOwned + Send + 'static,
400        F: FnOnce() -> Fut + Send + 'static,
401        Fut: Future<Output = Result<T, E>> + Send + 'static,
402        G: FnOnce(T) -> GFut + Send + 'static,
403        GFut: Future<Output = Result<(), DurableError>> + Send + 'static,
404    {
405        DurableContext::step_with_compensation(self, name, forward_fn, compensate_fn)
406    }
407
408    fn step_with_compensation_opts<T, E, F, Fut, G, GFut>(
409        &mut self,
410        name: &str,
411        options: StepOptions,
412        forward_fn: F,
413        compensate_fn: G,
414    ) -> impl Future<Output = Result<Result<T, E>, DurableError>> + Send
415    where
416        T: Serialize + DeserializeOwned + Send + 'static,
417        E: Serialize + DeserializeOwned + Send + 'static,
418        F: FnOnce() -> Fut + Send + 'static,
419        Fut: Future<Output = Result<T, E>> + Send + 'static,
420        G: FnOnce(T) -> GFut + Send + 'static,
421        GFut: Future<Output = Result<(), DurableError>> + Send + 'static,
422    {
423        DurableContext::step_with_compensation_opts(self, name, options, forward_fn, compensate_fn)
424    }
425
426    fn run_compensations(
427        &mut self,
428    ) -> impl Future<Output = Result<CompensationResult, DurableError>> + Send {
429        DurableContext::run_compensations(self)
430    }
431
432    fn callback_result<T: DeserializeOwned>(
433        &self,
434        handle: &CallbackHandle,
435    ) -> Result<T, DurableError> {
436        DurableContext::callback_result(self, handle)
437    }
438
439    fn execution_mode(&self) -> ExecutionMode {
440        DurableContext::execution_mode(self)
441    }
442
443    fn is_replaying(&self) -> bool {
444        DurableContext::is_replaying(self)
445    }
446
447    fn arn(&self) -> &str {
448        DurableContext::arn(self)
449    }
450
451    fn checkpoint_token(&self) -> &str {
452        DurableContext::checkpoint_token(self)
453    }
454
455    fn log(&self, message: &str) {
456        DurableContext::log(self, message);
457    }
458
459    fn log_with_data(&self, message: &str, data: &serde_json::Value) {
460        DurableContext::log_with_data(self, message, data);
461    }
462
463    fn log_debug(&self, message: &str) {
464        DurableContext::log_debug(self, message);
465    }
466
467    fn log_warn(&self, message: &str) {
468        DurableContext::log_warn(self, message);
469    }
470
471    fn log_error(&self, message: &str) {
472        DurableContext::log_error(self, message);
473    }
474
475    fn log_debug_with_data(&self, message: &str, data: &serde_json::Value) {
476        DurableContext::log_debug_with_data(self, message, data);
477    }
478
479    fn log_warn_with_data(&self, message: &str, data: &serde_json::Value) {
480        DurableContext::log_warn_with_data(self, message, data);
481    }
482
483    fn log_error_with_data(&self, message: &str, data: &serde_json::Value) {
484        DurableContext::log_error_with_data(self, message, data);
485    }
486
487    fn enable_batch_mode(&mut self) {
488        DurableContext::enable_batch_mode(self);
489    }
490
491    fn flush_batch(&mut self) -> impl Future<Output = Result<(), DurableError>> + Send {
492        DurableContext::flush_batch(self)
493    }
494}