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}