Skip to main content

dex_sdk/
sub_flow.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::any::TypeId;
10use std::time::Duration;
11
12use crate::start_flow_options::InitialAttribute;
13use crate::wait::SubFlowDefinition;
14use crate::{
15    Attribute, AttributeMap, Condition, Context, Flow, FlowConfig, FlowResult, FlowTimeoutPolicy,
16    HandlerResult, RetryPolicy, SdkResult, Value, value_mapper,
17};
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20/// Controls how a generated SubFlow Flow ID resolves an existing execution.
21pub enum SubFlowReusePolicy {
22    /// Attaches to a running execution or returns its existing terminal result.
23    Attach,
24    /// Restarts abnormal executions, attaches while running, and returns completed results.
25    RestartIfPreviousExitsAbnormally,
26    /// Replaces any different existing execution, including a running one.
27    AlwaysRestart,
28}
29
30#[derive(Clone)]
31/// Configures one durable SubFlow Condition.
32///
33/// The SubFlow inherits its parent's effective Flow configuration. Dex generates its Flow ID and
34/// request ID. Builder methods preserve normal Flow start defaults when omitted.
35pub struct SubFlowOptions {
36    pub(crate) timeout: Option<Duration>,
37    pub(crate) timeout_policy: FlowTimeoutPolicy,
38    pub(crate) start_delay: Option<Duration>,
39    pub(crate) retry_policy: Option<RetryPolicy>,
40    pub(crate) config_override: Option<FlowConfig>,
41    pub(crate) attributes: Vec<InitialAttribute>,
42    pub(crate) reuse_policy: SubFlowReusePolicy,
43    pub(crate) condition_id: Option<String>,
44}
45
46impl SubFlowOptions {
47    /// Creates options using the abnormal-restart reuse policy and normal start defaults.
48    pub fn new() -> Self {
49        Self {
50            timeout: None,
51            timeout_policy: FlowTimeoutPolicy::Default,
52            start_delay: None,
53            retry_policy: None,
54            config_override: None,
55            attributes: Vec::new(),
56            reuse_policy: SubFlowReusePolicy::RestartIfPreviousExitsAbnormally,
57            condition_id: None,
58        }
59    }
60
61    /// Sets the maximum total SubFlow execution duration.
62    pub fn timeout(mut self, value: Duration) -> Self {
63        self.timeout = Some(value);
64        self
65    }
66
67    /// Selects what Dex does when the positive soft SubFlow timeout expires.
68    pub fn timeout_policy(mut self, value: FlowTimeoutPolicy) -> Self {
69        self.timeout_policy = value;
70        self
71    }
72
73    /// Delays the SubFlow starting Step after server acceptance.
74    pub fn start_delay(mut self, value: Duration) -> Self {
75        self.start_delay = Some(value);
76        self
77    }
78
79    /// Sets whole-Flow retry behavior after abnormal completion.
80    pub fn retry_policy(mut self, value: RetryPolicy) -> Self {
81        self.retry_policy = Some(value);
82        self
83    }
84
85    /// Applies fields over the inherited parent Flow configuration.
86    pub fn config_override(mut self, value: FlowConfig) -> Self {
87        self.config_override = Some(value);
88        self
89    }
90
91    /// Selects existing-execution resolution behavior.
92    pub fn reuse_policy(mut self, value: SubFlowReusePolicy) -> Self {
93        self.reuse_policy = value;
94        self
95    }
96
97    /// Assigns the stable ID required by [`crate::Wait::any_combination_of`].
98    pub fn condition_id(mut self, value: impl Into<String>) -> Self {
99        self.condition_id = Some(value.into());
100        self
101    }
102
103    /// Adds one initial singleton Attribute owned by the target SubFlow.
104    pub fn initial_attribute<T: Value>(mut self, attribute: &Attribute<T>, value: T) -> Self {
105        self.attributes
106            .push(InitialAttribute::for_attribute(attribute, value));
107        self
108    }
109
110    /// Adds one initial Attribute-map value for an instance.
111    /// Slash is prohibited in instance keys because it is a reserved character.
112    pub fn initial_attribute_map<T: Value>(
113        mut self,
114        attribute: &AttributeMap<T>,
115        instance: &str,
116        value: T,
117    ) -> Self {
118        self.attributes.push(InitialAttribute::for_attribute_map(
119            attribute, instance, value,
120        ));
121        self
122    }
123}
124
125impl Default for SubFlowOptions {
126    fn default() -> Self {
127        Self::new()
128    }
129}
130
131/// Creates durable SubFlow Conditions and reads their Execute results.
132pub struct SubFlow;
133
134impl SubFlow {
135    /// Creates a SubFlow Condition with default options.
136    ///
137    /// # Errors
138    ///
139    /// Returns a value-mapping error when `input` cannot be encoded.
140    pub fn run<SomeFlow: Flow>(
141        flow: &SomeFlow,
142        input: SomeFlow::StartInput,
143    ) -> SdkResult<Condition> {
144        Self::run_with_options(flow, input, SubFlowOptions::new())
145    }
146
147    /// Creates a SubFlow Condition with explicit options.
148    ///
149    /// # Errors
150    ///
151    /// Returns a value-mapping error when `input` cannot be encoded. Worker mapping later validates
152    /// the exact registered Rust Flow type, starting Step, Attributes, and option durations.
153    pub fn run_with_options<SomeFlow: Flow>(
154        flow: &SomeFlow,
155        input: SomeFlow::StartInput,
156        options: SubFlowOptions,
157    ) -> SdkResult<Condition> {
158        Ok(Condition::sub_flow(SubFlowDefinition {
159            flow_type: flow.flow_type(),
160            type_id: TypeId::of::<SomeFlow>(),
161            input: value_mapper::encode(&input)?,
162            options,
163        }))
164    }
165
166    /// Returns the first SubFlow result during Step `execute`.
167    pub fn condition_result(context: &Context) -> HandlerResult<FlowResult> {
168        Self::condition_result_at(context, 0)
169    }
170
171    /// Returns one stable-indexed SubFlow result during Step `execute`.
172    pub fn condition_result_at(context: &Context, index: usize) -> HandlerResult<FlowResult> {
173        context.sub_flow_result(index)
174    }
175
176    /// Returns the generated Flow ID for the first SubFlow Condition during Step `execute`.
177    pub fn flow_id(context: &Context) -> HandlerResult<String> {
178        Self::flow_id_at(context, 0)
179    }
180
181    /// Returns the generated Flow ID for one stable-indexed SubFlow Condition during `execute`.
182    pub fn flow_id_at(context: &Context, index: usize) -> HandlerResult<String> {
183        context.sub_flow_id(index)
184    }
185}