use async_trait::async_trait;
use floxide::*;
#[derive(Clone, Debug)]
pub struct AlwaysFailNode;
#[async_trait]
impl Node for AlwaysFailNode {
type Input = ();
type Output = String;
async fn process(
&self,
_ctx: &(),
_input: (),
) -> Result<Transition<Self::Output>, FloxideError> {
println!("AlwaysFailNode: intentional failure");
Err(FloxideError::Generic("intentional failure".into()))
}
}
#[derive(Clone, Debug)]
pub struct RecoveryNode;
#[async_trait]
impl Node for RecoveryNode {
type Input = ();
type Output = String;
async fn process(
&self,
_ctx: &(),
_input: (),
) -> Result<Transition<Self::Output>, FloxideError> {
println!("RecoveryNode: recovery");
Ok(Transition::Next("recovered".into()))
}
}
workflow! {
pub struct ErrorFallbackWorkflow {
fail: AlwaysFailNode,
recovery: RecoveryNode,
}
start = fail;
edges {
fail => {};
fail on_failure => { [ recovery ] };
recovery => {};
}
}
pub async fn run_error_fallback_workflow() -> Result<String, Box<dyn std::error::Error>> {
let wf = ErrorFallbackWorkflow {
fail: AlwaysFailNode,
recovery: RecoveryNode,
};
let ctx = WorkflowCtx::new(());
let output: String = wf.run(&ctx, ()).await?;
Ok(output)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();
let output = run_error_fallback_workflow().await?;
println!("Workflow output after fallback: {}", output);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_error_fallback_workflow() {
let output = run_error_fallback_workflow()
.await
.expect("workflow should succeed after fallback");
assert_eq!(output, "recovered");
}
}