Skip to main content

dex_sdk/
step_options.rs

1// Copyright (c) 2026 Super Durable, Inc.
2//
3// Licensed under the Super Durable Source License 1.0.
4// You may not use this file except in compliance with the License.
5// See the LICENSE file in the repository root.
6//
7// SPDX-License-Identifier: LicenseRef-Super-Durable-1.0
8
9use std::marker::PhantomData;
10use std::time::Duration;
11
12use crate::attribute::AttributeLock;
13use crate::{RetryPolicy, Step, Value};
14
15/// Configures one Step's handler execution and persistence behavior.
16///
17/// New options preserve server timeout, retry, and durability defaults. `wait_for` failures fail the
18/// Flow by default. Attribute locks apply only to the matching handler call. The type system checks
19/// that an execute-failure recovery Step accepts the same input type.
20///
21/// # Examples
22///
23/// ```
24/// use dex_sdk::{Attribute, RetryPolicy, StepOptions, WaitForFailurePolicy};
25/// use std::time::Duration;
26///
27/// let balance = Attribute::<i64>::new("balance");
28/// let options = StepOptions::<String>::new()
29///     .execute_method_timeout(Duration::from_secs(30))
30///     .execute_retry(RetryPolicy::new().maximum_attempts(3))
31///     .wait_for_failure(WaitForFailurePolicy::Proceed)
32///     .execute_lock(balance.lock());
33/// ```
34pub struct StepOptions<Input> {
35    pub(crate) wait_for_method_timeout: Option<Duration>,
36    pub(crate) execute_method_timeout: Option<Duration>,
37    pub(crate) wait_for_retry: Option<RetryPolicy>,
38    pub(crate) execute_retry: Option<RetryPolicy>,
39    pub(crate) wait_for_failure: WaitForFailurePolicy,
40    pub(crate) wait_for_durability: StepDurability,
41    pub(crate) execute_durability: StepDurability,
42    pub(crate) wait_for_locks: Vec<AttributeLock>,
43    pub(crate) execute_locks: Vec<AttributeLock>,
44    pub(crate) execute_failure_step: Option<&'static str>,
45    marker: PhantomData<fn(Input)>,
46}
47
48#[derive(Clone)]
49pub(crate) struct ErasedStepOptions {
50    pub(crate) wait_for_method_timeout: Option<Duration>,
51    pub(crate) execute_method_timeout: Option<Duration>,
52    pub(crate) wait_for_retry: Option<RetryPolicy>,
53    pub(crate) execute_retry: Option<RetryPolicy>,
54    pub(crate) wait_for_failure: WaitForFailurePolicy,
55    pub(crate) wait_for_durability: StepDurability,
56    pub(crate) execute_durability: StepDurability,
57    pub(crate) wait_for_locks: Vec<AttributeLock>,
58    pub(crate) execute_locks: Vec<AttributeLock>,
59    pub(crate) execute_failure_step: Option<&'static str>,
60}
61
62impl<Input> From<StepOptions<Input>> for ErasedStepOptions {
63    fn from(options: StepOptions<Input>) -> Self {
64        Self {
65            wait_for_method_timeout: options.wait_for_method_timeout,
66            execute_method_timeout: options.execute_method_timeout,
67            wait_for_retry: options.wait_for_retry,
68            execute_retry: options.execute_retry,
69            wait_for_failure: options.wait_for_failure,
70            wait_for_durability: options.wait_for_durability,
71            execute_durability: options.execute_durability,
72            wait_for_locks: options.wait_for_locks,
73            execute_locks: options.execute_locks,
74            execute_failure_step: options.execute_failure_step,
75        }
76    }
77}
78
79impl<Input: Value> StepOptions<Input> {
80    /// Creates options that preserve server defaults and fail on exhausted `wait_for` retries.
81    pub fn new() -> Self {
82        Self {
83            wait_for_method_timeout: None,
84            execute_method_timeout: None,
85            wait_for_retry: None,
86            execute_retry: None,
87            wait_for_failure: WaitForFailurePolicy::FailFlow,
88            wait_for_durability: StepDurability::Default,
89            execute_durability: StepDurability::Default,
90            wait_for_locks: Vec::new(),
91            execute_locks: Vec::new(),
92            execute_failure_step: None,
93            marker: PhantomData,
94        }
95    }
96
97    /// Sets the maximum duration of one `wait_for` handler attempt.
98    pub fn wait_for_method_timeout(mut self, value: Duration) -> Self {
99        self.wait_for_method_timeout = Some(value);
100        self
101    }
102
103    /// Sets the maximum duration of one `execute` handler attempt.
104    pub fn execute_method_timeout(mut self, value: Duration) -> Self {
105        self.execute_method_timeout = Some(value);
106        self
107    }
108
109    /// Sets the retry policy for failed `wait_for` attempts.
110    pub fn wait_for_retry(mut self, value: RetryPolicy) -> Self {
111        self.wait_for_retry = Some(value);
112        self
113    }
114
115    /// Sets the retry policy for failed `execute` attempts.
116    pub fn execute_retry(mut self, value: RetryPolicy) -> Self {
117        self.execute_retry = Some(value);
118        self
119    }
120
121    /// Selects whether exhausted `wait_for` retries fail or advance the Flow.
122    pub fn wait_for_failure(mut self, value: WaitForFailurePolicy) -> Self {
123        self.wait_for_failure = value;
124        self
125    }
126
127    /// Routes exhausted `execute` failures to a recovery Step with the same input type.
128    pub fn on_execute_failure_proceed_to<RecoveryStep>(mut self, step: &RecoveryStep) -> Self
129    where
130        RecoveryStep: Step<Input = Input>,
131    {
132        self.execute_failure_step = Some(step.step_type());
133        self
134    }
135
136    /// Overrides persistence durability for writes staged by `wait_for`.
137    pub fn wait_for_durability(mut self, value: StepDurability) -> Self {
138        self.wait_for_durability = value;
139        self
140    }
141
142    /// Overrides persistence durability for writes staged by `execute`.
143    pub fn execute_durability(mut self, value: StepDurability) -> Self {
144        self.execute_durability = value;
145        self
146    }
147
148    /// Adds an Attribute lock held for the `wait_for` invocation.
149    pub fn wait_for_lock(mut self, value: AttributeLock) -> Self {
150        self.wait_for_locks.push(value);
151        self
152    }
153
154    /// Adds an Attribute lock held for the `execute` invocation.
155    pub fn execute_lock(mut self, value: AttributeLock) -> Self {
156        self.execute_locks.push(value);
157        self
158    }
159}
160
161impl<Input: Value> Default for StepOptions<Input> {
162    fn default() -> Self {
163        Self::new()
164    }
165}
166
167#[derive(Clone, Copy, Debug, Eq, PartialEq)]
168/// Selects the terminal behavior after `wait_for` exhausts its retry policy.
169pub enum WaitForFailurePolicy {
170    /// Fails the Flow.
171    FailFlow,
172    /// Skips the failed wait and invokes `execute` with failure visible in [`crate::Context`].
173    Proceed,
174}
175
176#[derive(Clone, Copy, Debug, Eq, PartialEq)]
177/// Controls when staged Step persistence must be durably acknowledged.
178pub enum StepDurability {
179    /// Uses the Flow or server default.
180    Default,
181    /// Waits for durable persistence before completing the handler request.
182    Sync,
183    /// Allows asynchronous persistence after accepting the handler result.
184    Async,
185}