Skip to main content

ranvier_runtime/
closure_transition.rs

1//! # Closure-based Transition
2//!
3//! Provides `ClosureTransition`, a lightweight wrapper that implements
4//! the `Transition` trait for synchronous closures. This eliminates
5//! boilerplate for simple data-mapping or validation steps.
6//!
7//! ## Usage
8//!
9//! ```rust
10//! use ranvier_runtime::prelude::*;
11//! use ranvier_core::prelude::*;
12//!
13//! let pipeline = Axon::simple::<String>("pipeline")
14//!     .then_fn("double", |input: (), bus: &mut Bus| {
15//!         Outcome::next("result".to_string())
16//!     });
17//! ```
18
19use async_trait::async_trait;
20use ranvier_core::bus::Bus;
21use ranvier_core::outcome::Outcome;
22use ranvier_core::transition::{ResourceRequirement, Transition};
23use std::fmt::Debug;
24use std::marker::PhantomData;
25
26/// A wrapper that adapts a synchronous closure into a `Transition`.
27///
28/// The closure receives `(From, &mut Bus)` and returns `Outcome<To, E>`.
29/// Resources are not passed to the closure — closures that need typed
30/// resources should use a full `#[transition]` struct instead.
31///
32/// The `Res` type parameter allows `ClosureTransition` to be used in
33/// pipelines with any resource type — the resource is simply ignored
34/// during execution.
35pub struct ClosureTransition<F, Res = ()> {
36    label: String,
37    f: F,
38    _phantom: PhantomData<Res>,
39}
40
41impl<F, Res> ClosureTransition<F, Res> {
42    /// Create a new closure transition with the given label.
43    pub fn new(label: impl Into<String>, f: F) -> Self {
44        Self {
45            label: label.into(),
46            f,
47            _phantom: PhantomData,
48        }
49    }
50}
51
52impl<F: Clone, Res> Clone for ClosureTransition<F, Res> {
53    fn clone(&self) -> Self {
54        Self {
55            label: self.label.clone(),
56            f: self.f.clone(),
57            _phantom: PhantomData,
58        }
59    }
60}
61
62#[async_trait]
63impl<F, From, To, E, Res> Transition<From, To> for ClosureTransition<F, Res>
64where
65    F: Fn(From, &mut Bus) -> Outcome<To, E> + Send + Sync + 'static,
66    From: Send + 'static,
67    To: Send + 'static,
68    E: Send + Sync + Debug + 'static,
69    Res: ResourceRequirement,
70{
71    type Error = E;
72    type Resources = Res;
73
74    fn label(&self) -> String {
75        self.label.clone()
76    }
77
78    async fn run(
79        &self,
80        state: From,
81        _resources: &Self::Resources,
82        bus: &mut Bus,
83    ) -> Outcome<To, Self::Error> {
84        (self.f)(state, bus)
85    }
86}