Skip to main content

backbone_core/
flow.rs

1//! Workflow step and context traits for saga-pattern workflows.
2//!
3//! **Deprecated.** Nothing used these types: an in-memory instance with no persistence, wait, timer
4//! or compensation. A workflow a schema declares with `config.executor: engine` runs through
5//! [`crate::workflow`]: the generated spec and definition, and a [`WorkflowEngine`](crate::workflow)
6//! over Postgres. This module stays for one minor release so code that names it still builds, and is
7//! removed after it.
8//!
9//! Phase 0 generic base for the `flow.rs` generator (Category C).
10//!
11//! Every generated `{Name}Workflow` used to re-define the same
12//! structural boilerplate. This module provides the single generic version so
13//! that generated files collapse to type aliases:
14//!
15//! ```rust,ignore
16//! use backbone_core::flow::{
17//!     FlowError, FlowStatus, FlowInstance, FlowExecutor,
18//!     WorkflowStep, WorkflowContext,
19//! };
20//! use crate::application::workflows::order_processing_workflow::OrderProcessingFlowStep;
21//!
22//! pub type OrderProcessingFlowError    = FlowError;
23//! pub type OrderProcessingFlowStatus   = FlowStatus;
24//! pub type OrderProcessingFlowInstance = FlowInstance<OrderProcessingFlowStep>;
25//!
26//! pub struct OrderProcessingFlowExecutor<H>(FlowExecutor<H>);
27//! // impl execute_step() — the only unique part — stays in the generated file
28//! ```
29
30#![allow(deprecated)]
31
32use std::sync::Arc;
33
34// ─── FlowError ───────────────────────────────────────────────────────────────
35
36#[deprecated(since = "3.16.0", note = "nothing used it; declared workflows run through backbone_core::workflow (WorkflowSpec, WorkflowDefinition and WorkflowEngine)")]
37/// Common workflow execution error — identical for every workflow.
38///
39/// Generated files use a type alias: `pub type OrderProcessingFlowError = FlowError;`
40#[derive(Debug, Clone, thiserror::Error)]
41pub enum FlowError {
42    #[error("No current step to execute")]
43    NoCurrentStep,
44
45    #[error("Step execution failed: {0}")]
46    StepFailed(String),
47
48    #[error("Condition evaluation failed: {0}")]
49    ConditionFailed(String),
50
51    #[error("Compensation failed: {0}")]
52    CompensationFailed(String),
53
54    #[error("Flow timed out")]
55    Timeout,
56
57    #[error("Flow cancelled")]
58    Cancelled,
59
60    #[error("Invalid state transition: {from} -> {to}")]
61    InvalidTransition { from: String, to: String },
62}
63
64// ─── FlowStatus ──────────────────────────────────────────────────────────────
65
66#[deprecated(since = "3.16.0", note = "nothing used it; declared workflows run through backbone_core::workflow (WorkflowSpec, WorkflowDefinition and WorkflowEngine)")]
67/// Workflow execution status — identical for every workflow.
68///
69/// Generated files use a type alias: `pub type OrderProcessingFlowStatus = FlowStatus;`
70#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
71#[serde(rename_all = "snake_case")]
72pub enum FlowStatus {
73    /// Flow is pending execution
74    Pending,
75    /// Flow is currently running
76    Running,
77    /// Flow is waiting for an event or condition
78    Waiting,
79    /// Flow completed successfully
80    Completed,
81    /// Flow failed
82    Failed,
83    /// Flow was cancelled
84    Cancelled,
85    /// Flow is compensating (rolling back)
86    Compensating,
87}
88
89// ─── FlowInstance ─────────────────────────────────────────────────────────────
90
91/// Generic workflow instance parametrised over the step enum `S`.
92///
93/// Generated files use a type alias:
94/// `pub type OrderProcessingFlowInstance = FlowInstance<OrderProcessingFlowStep>;`
95#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
96#[serde(bound(
97    serialize = "S: serde::Serialize",
98    deserialize = "S: serde::de::DeserializeOwned",
99))]
100#[deprecated(since = "3.16.0", note = "nothing used it; declared workflows run through backbone_core::workflow (WorkflowSpec, WorkflowDefinition and WorkflowEngine)")]
101pub struct FlowInstance<S>
102where
103    S: std::fmt::Debug + Clone + serde::Serialize + serde::de::DeserializeOwned,
104{
105    pub id: String,
106    pub status: FlowStatus,
107    pub current_step: Option<S>,
108    pub context: serde_json::Value,
109    pub completed_steps: Vec<S>,
110    pub error: Option<String>,
111    pub created_at: chrono::DateTime<chrono::Utc>,
112    pub updated_at: chrono::DateTime<chrono::Utc>,
113}
114
115impl<S> FlowInstance<S>
116where
117    S: std::fmt::Debug + Clone + serde::Serialize + serde::de::DeserializeOwned,
118{
119    pub fn new(id: impl Into<String>) -> Self {
120        let now = chrono::Utc::now();
121        Self {
122            id: id.into(),
123            status: FlowStatus::Pending,
124            current_step: None,
125            context: serde_json::json!({}),
126            completed_steps: Vec::new(),
127            error: None,
128            created_at: now,
129            updated_at: now,
130        }
131    }
132
133    pub fn is_complete(&self) -> bool {
134        matches!(
135            self.status,
136            FlowStatus::Completed | FlowStatus::Failed | FlowStatus::Cancelled
137        )
138    }
139
140    pub fn is_running(&self) -> bool {
141        matches!(self.status, FlowStatus::Running | FlowStatus::Waiting)
142    }
143
144    pub fn set_context(&mut self, key: &str, value: serde_json::Value) {
145        if let serde_json::Value::Object(ref mut map) = self.context {
146            map.insert(key.to_string(), value);
147        }
148        self.updated_at = chrono::Utc::now();
149    }
150
151    pub fn get_context(&self, key: &str) -> Option<&serde_json::Value> {
152        if let serde_json::Value::Object(ref map) = self.context {
153            map.get(key)
154        } else {
155            None
156        }
157    }
158}
159
160// ─── WorkflowContext impl on FlowInstance ────────────────────────────────────
161
162impl<S> WorkflowContext<FlowInstance<S>> for FlowInstance<S>
163where
164    S: std::fmt::Debug + Clone + serde::Serialize + serde::de::DeserializeOwned + Send + Sync,
165{
166    fn entity(&self) -> &FlowInstance<S> { self }
167    fn set_var(&mut self, key: &str, value: serde_json::Value) { self.set_context(key, value); }
168    fn get_var(&self, key: &str) -> Option<&serde_json::Value> { self.get_context(key) }
169}
170
171// ─── FlowExecutor ────────────────────────────────────────────────────────────
172
173#[deprecated(since = "3.16.0", note = "nothing used it; declared workflows run through backbone_core::workflow (WorkflowSpec, WorkflowDefinition and WorkflowEngine)")]
174/// Generic executor shell — holds the handler, provides cancel/fail helpers.
175///
176/// Generated files wrap this with a newtype and add the unique `execute_step()`:
177///
178/// ```rust,ignore
179/// pub struct OrderProcessingFlowExecutor<H: OrderProcessingStepHandler>(FlowExecutor<H>);
180///
181/// impl<H: OrderProcessingStepHandler> OrderProcessingFlowExecutor<H> {
182///     pub fn new(handler: Arc<H>) -> Self { Self(FlowExecutor::new(handler)) }
183///     pub fn handler(&self) -> &Arc<H> { &self.0.handler }
184///
185///     pub async fn execute_step(&self, instance: &mut OrderProcessingFlowInstance)
186///         -> Result<(), FlowError> { /* unique dispatch */ }
187///
188///     pub async fn start(&self, id: impl Into<String>)
189///         -> Result<OrderProcessingFlowInstance, FlowError> { self.0.start(id) }
190///
191///     pub async fn run(&self, instance: &mut OrderProcessingFlowInstance)
192///         -> Result<(), FlowError> {
193///         while !instance.is_complete() && instance.status != FlowStatus::Waiting {
194///             self.execute_step(instance).await?;
195///         }
196///         Ok(())
197///     }
198///
199///     pub fn cancel(&self, instance: &mut OrderProcessingFlowInstance) {
200///         self.0.cancel(instance);
201///     }
202///     pub fn fail(&self, instance: &mut OrderProcessingFlowInstance, error: impl Into<String>) {
203///         self.0.fail(instance, error);
204///     }
205/// }
206/// ```
207pub struct FlowExecutor<H> {
208    pub handler: Arc<H>,
209}
210
211impl<H: Send + Sync + 'static> FlowExecutor<H> {
212    pub fn new(handler: Arc<H>) -> Self {
213        Self { handler }
214    }
215
216    /// Start a new flow instance at a given step.
217    pub fn start<S>(&self, id: impl Into<String>, first_step: S) -> FlowInstance<S>
218    where
219        S: std::fmt::Debug + Clone + serde::Serialize + serde::de::DeserializeOwned,
220    {
221        let mut instance = FlowInstance::new(id);
222        instance.status = FlowStatus::Running;
223        instance.current_step = Some(first_step);
224        instance
225    }
226
227    /// Cancel the flow instance.
228    pub fn cancel<S>(&self, instance: &mut FlowInstance<S>)
229    where
230        S: std::fmt::Debug + Clone + serde::Serialize + serde::de::DeserializeOwned,
231    {
232        instance.status = FlowStatus::Cancelled;
233        instance.updated_at = chrono::Utc::now();
234    }
235
236    /// Mark the flow instance as failed with a message.
237    pub fn fail<S>(&self, instance: &mut FlowInstance<S>, error: impl Into<String>)
238    where
239        S: std::fmt::Debug + Clone + serde::Serialize + serde::de::DeserializeOwned,
240    {
241        instance.status = FlowStatus::Failed;
242        instance.error = Some(error.into());
243        instance.updated_at = chrono::Utc::now();
244    }
245}
246
247// ─── Traits ──────────────────────────────────────────────────────────────────
248
249#[deprecated(since = "3.16.0", note = "nothing used it; declared workflows run through backbone_core::workflow (WorkflowSpec, WorkflowDefinition and WorkflowEngine)")]
250/// Workflow execution context for an entity `E`.
251pub trait WorkflowContext<E>: Send + Sync {
252    fn entity(&self) -> &E;
253    fn set_var(&mut self, key: &str, value: serde_json::Value);
254    fn get_var(&self, key: &str) -> Option<&serde_json::Value>;
255}
256
257#[deprecated(since = "3.16.0", note = "nothing used it; declared workflows run through backbone_core::workflow (WorkflowSpec, WorkflowDefinition and WorkflowEngine)")]
258/// A named step within a workflow.
259pub trait WorkflowStep: Send + Sync {
260    fn name(&self) -> &'static str;
261}