1use async_trait::async_trait;
22use floxide_core::{error::FloxideError, ActionType, LifecycleNode, NodeId};
23use futures::future::BoxFuture;
24use std::fmt::Debug;
25use std::marker::PhantomData;
26use uuid::Uuid;
27
28#[async_trait]
86pub trait TransformNode<Input, Output, Error>: Send + Sync
87where
88 Input: Send + 'static,
89 Output: Send + 'static,
90 Error: std::error::Error + Send + Sync + 'static,
91{
92 async fn prep(&self, input: Input) -> Result<Input, Error>;
94
95 async fn exec(&self, input: Input) -> Result<Output, Error>;
97
98 async fn post(&self, output: Output) -> Result<Output, Error>;
100}
101
102pub struct TransformNodeAdapter<TN, Input, Output, Error, Action>
104where
105 TN: TransformNode<Input, Output, Error>,
106 Input: Clone + Send + Sync + 'static,
107 Output: Clone + Send + Sync + 'static,
108 Error: std::error::Error + Send + Sync + 'static,
109 Action: ActionType + Default + Send + Sync + 'static,
110{
111 node: TN,
112 id: NodeId,
113 _phantom: PhantomData<(Input, Output, Error, Action)>,
114}
115
116impl<TN, Input, Output, Error, Action> TransformNodeAdapter<TN, Input, Output, Error, Action>
117where
118 TN: TransformNode<Input, Output, Error>,
119 Input: Clone + Send + Sync + 'static,
120 Output: Clone + Send + Sync + 'static,
121 Error: std::error::Error + Send + Sync + 'static,
122 Action: ActionType + Default + Send + Sync + 'static,
123{
124 pub fn new(node: TN) -> Self {
126 Self {
127 node,
128 id: Uuid::new_v4().to_string(),
129 _phantom: PhantomData,
130 }
131 }
132
133 pub fn with_id(node: TN, id: impl Into<String>) -> Self {
135 Self {
136 node,
137 id: id.into(),
138 _phantom: PhantomData,
139 }
140 }
141}
142
143impl<TN, Input, Output, Error, Action> Debug
144 for TransformNodeAdapter<TN, Input, Output, Error, Action>
145where
146 TN: TransformNode<Input, Output, Error> + Debug,
147 Input: Clone + Send + Sync + 'static,
148 Output: Clone + Send + Sync + 'static,
149 Error: std::error::Error + Send + Sync + 'static,
150 Action: ActionType + Default + Send + Sync + 'static,
151{
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 f.debug_struct("TransformNodeAdapter")
154 .field("node", &self.node)
155 .field("id", &self.id)
156 .finish()
157 }
158}
159
160#[derive(Debug, Clone)]
162pub struct TransformContext<Input> {
163 pub input: Input,
164}
165
166impl<Input> TransformContext<Input> {
167 pub fn new(input: Input) -> Self {
169 Self { input }
170 }
171}
172
173#[async_trait]
174impl<TN, Input, Output, Error, Action> LifecycleNode<TransformContext<Input>, Action>
175 for TransformNodeAdapter<TN, Input, Output, Error, Action>
176where
177 TN: TransformNode<Input, Output, Error> + Send + Sync + 'static,
178 Input: Clone + Send + Sync + 'static,
179 Output: Clone + Send + Sync + 'static,
180 Error: std::error::Error + Send + Sync + 'static + Into<FloxideError>,
181 Action: ActionType + Default + Send + Sync + 'static,
182{
183 type PrepOutput = Input;
184 type ExecOutput = Output;
185
186 fn id(&self) -> NodeId {
187 self.id.clone()
188 }
189
190 async fn prep(
191 &self,
192 ctx: &mut TransformContext<Input>,
193 ) -> Result<Self::PrepOutput, FloxideError> {
194 self.node
195 .prep(ctx.input.clone())
196 .await
197 .map_err(|e| e.into())
198 }
199
200 async fn exec(&self, prep_result: Self::PrepOutput) -> Result<Self::ExecOutput, FloxideError> {
201 self.node.exec(prep_result).await.map_err(|e| e.into())
202 }
203
204 async fn post(
205 &self,
206 _prep_result: Self::PrepOutput,
207 exec_result: Self::ExecOutput,
208 _ctx: &mut TransformContext<Input>,
209 ) -> Result<Action, FloxideError> {
210 let _result = self.node.post(exec_result).await.map_err(|e| e.into())?;
211 Ok(Action::default())
212 }
213}
214
215pub fn transform_node<P, E, Po, I, O, Err>(
217 prep_fn: P,
218 exec_fn: E,
219 post_fn: Po,
220) -> impl TransformNode<I, O, Err>
221where
222 I: Clone + Send + Sync + 'static,
223 O: Clone + Send + Sync + 'static,
224 Err: std::error::Error + Send + Sync + 'static,
225 P: Fn(I) -> BoxFuture<'static, Result<I, Err>> + Send + Sync + 'static,
226 E: Fn(I) -> BoxFuture<'static, Result<O, Err>> + Send + Sync + 'static,
227 Po: Fn(O) -> BoxFuture<'static, Result<O, Err>> + Send + Sync + 'static,
228{
229 struct ClosureTransformNode<P, E, Po, I, O, Err> {
230 prep_fn: P,
231 exec_fn: E,
232 post_fn: Po,
233 _phantom: PhantomData<(I, O, Err)>,
234 }
235
236 impl<P, E, Po, I, O, Err> Debug for ClosureTransformNode<P, E, Po, I, O, Err> {
237 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
238 f.debug_struct("ClosureTransformNode").finish()
239 }
240 }
241
242 #[async_trait]
243 impl<P, E, Po, I, O, Err> TransformNode<I, O, Err> for ClosureTransformNode<P, E, Po, I, O, Err>
244 where
245 I: Clone + Send + Sync + 'static,
246 O: Clone + Send + Sync + 'static,
247 Err: std::error::Error + Send + Sync + 'static,
248 P: Fn(I) -> BoxFuture<'static, Result<I, Err>> + Send + Sync + 'static,
249 E: Fn(I) -> BoxFuture<'static, Result<O, Err>> + Send + Sync + 'static,
250 Po: Fn(O) -> BoxFuture<'static, Result<O, Err>> + Send + Sync + 'static,
251 {
252 async fn prep(&self, input: I) -> Result<I, Err> {
253 (self.prep_fn)(input).await
254 }
255
256 async fn exec(&self, input: I) -> Result<O, Err> {
257 (self.exec_fn)(input).await
258 }
259
260 async fn post(&self, output: O) -> Result<O, Err> {
261 (self.post_fn)(output).await
262 }
263 }
264
265 ClosureTransformNode {
266 prep_fn,
267 exec_fn,
268 post_fn,
269 _phantom: PhantomData,
270 }
271}
272
273pub fn to_lifecycle_node<TN, I, O, Err, A>(
275 transform_node: TN,
276) -> impl LifecycleNode<TransformContext<I>, A, PrepOutput = I, ExecOutput = O>
277where
278 TN: TransformNode<I, O, Err> + Send + Sync + 'static,
279 I: Clone + Send + Sync + 'static,
280 O: Clone + Send + Sync + 'static,
281 Err: std::error::Error + Send + Sync + 'static + Into<FloxideError>,
282 A: ActionType + Default + Send + Sync + 'static,
283{
284 TransformNodeAdapter::<TN, I, O, Err, A>::new(transform_node)
285}
286
287pub fn create_transform_node<I, O, Err>(
289 prep_fn: impl Fn(I) -> BoxFuture<'static, Result<I, Err>> + Send + Sync + 'static,
290 exec_fn: impl Fn(I) -> BoxFuture<'static, Result<O, Err>> + Send + Sync + 'static,
291 post_fn: impl Fn(O) -> BoxFuture<'static, Result<O, Err>> + Send + Sync + 'static,
292) -> impl TransformNode<I, O, Err>
293where
294 I: Clone + Send + Sync + 'static,
295 O: Clone + Send + Sync + 'static,
296 Err: std::error::Error + Send + Sync + 'static,
297{
298 transform_node(prep_fn, exec_fn, post_fn)
299}