1use futures::{future::BoxFuture, FutureExt};
141
142fn to_fn_error<E1, E2>(error:E1) -> E2 where E2:From<E1>{
143 From::from(error)
144}
145
146
147pub use function_compose_proc_macros::*;
148pub use paste::*;
149pub use concat_idents::concat_idents;
150
151macro_rules! composer_generator {
152 ($arg1:ident, $return_type1:ident, $return_type2:ident, $error_type1:ident, $error_type2:ident) => {
153 paste!{
154 #[doc = concat!("Then implementation for composing sync function (BoxedFn1) with another sync function(BoxedFn1) ")]
155 impl<'a, $arg1: 'a + Send, $return_type1: 'a + Send, $return_type2: 'a, $error_type1: Send + 'a, $error_type2: Send + 'a>
156 Then<'a, $arg1, $return_type1, $return_type2, BoxedFn1<'a, $return_type1, $return_type2, $error_type2>, BoxedFn1<'a, $arg1, $return_type2, $error_type2>> for BoxedFn1<'a, $arg1, $return_type1, $error_type1> where E2:From<E1>{
157
158 fn then(self, f: BoxedFn1<'a, $return_type1, $return_type2, $error_type2>) -> BoxedFn1<'a, $arg1, $return_type2, $error_type2> {
159 let r1 = move |x: $arg1| {
160 let g_result = self(x);
161 match g_result{
162 Ok(inner_result) => f(inner_result),
163 Err(error) => Err(to_fn_error(error)),
164 }
165 };
166 Box::new(r1)
167 }
168 }
169
170 #[doc = concat!("Then implementation for composing sync function(BoxedFn1) with another async function(BoxedAsyncFn1) ")]
171 impl<'a, $arg1: 'a + Send, $return_type1: 'a + Send, $return_type2: 'a, $error_type1: Send + 'a, $error_type2: Send + 'a>
172 Then<'a, $arg1, $return_type1, $return_type2, BoxedAsyncFn1<'a, $return_type1, $return_type2, $error_type2>, BoxedAsyncFn1<'a, $arg1, $return_type2, $error_type2>> for BoxedFn1<'a, $arg1, $return_type1, $error_type1> where E2:From<E1>{
173
174 fn then(self, f: BoxedAsyncFn1<'a, $return_type1, $return_type2, $error_type2>) -> BoxedAsyncFn1<'a, $arg1, $return_type2, $error_type2> {
175 let r1 = |x: $arg1| {
176 async move{
177 let g_result = self(x);
178 match g_result{
179 Ok(inner_result) => f(inner_result).await,
180 Err(error) => Err(to_fn_error(error)),
181 }
182 }.boxed()
184 };
185 Box::new(r1)
186 }
187 }
188
189 #[doc = concat!("Then implementation for composing async function(BoxedAsyncFn1) with another sync function(BoxedFn1) ")]
190
191 impl<'a, $arg1: 'a + Send, $return_type1: 'a + Send, $return_type2: 'a, $error_type1:Send + 'a, $error_type2:Send + 'a>
192 Then<'a, $arg1, $return_type1, $return_type2, BoxedFn1<'a, $return_type1, $return_type2, $error_type2>, BoxedAsyncFn1<'a, $arg1, $return_type2, $error_type2>> for BoxedAsyncFn1<'a, $arg1, $return_type1, $error_type1> where E2:From<E1>{
193
194 fn then(self, f: BoxedFn1<'a, $return_type1, $return_type2, $error_type2>) -> BoxedAsyncFn1<'a, $arg1, $return_type2, $error_type2> {
195 let r1 = |a: $arg1| {
196 async move {
197 let g_result = self(a).await;
198 match g_result{
199 Ok(inner_result) => f(inner_result),
200 Err(error) => Err(to_fn_error(error)),
201 }
202 }.boxed()
203 };
204 let r: BoxedAsyncFn1<'a,$arg1, $return_type2, $error_type2> = Box::new(r1);
205 r
206 }
207 }
208
209
210 #[doc = concat!("Then implementation for composing async function(BoxedAsyncFn1) with another async function(BoxedAsyncFn1) ")]
211 impl<'a, $arg1: 'a + Send, $return_type1: 'a + Send, $return_type2: 'a, $error_type1:Send + 'a, $error_type2:Send + 'a>
212 Then<'a, $arg1, $return_type1, $return_type2, BoxedAsyncFn1<'a, $return_type1, $return_type2, $error_type2>, BoxedAsyncFn1<'a, $arg1, $return_type2, $error_type2>> for BoxedAsyncFn1<'a, $arg1, $return_type1, $error_type1> where E2:From<E1>{
213
214 fn then(self, f: BoxedAsyncFn1<'a, $return_type1, $return_type2, $error_type2>) -> BoxedAsyncFn1<'a, $arg1, $return_type2, $error_type2> {
215 let r1 = |a: $arg1| {
216 async move {
217 let g_result = self(a).await;
218 match g_result{
219 Ok(inner_result) => f(inner_result).await,
220 Err(error) => Err(to_fn_error(error)),
221 }
222 }.boxed()
223 };
224 let r: BoxedAsyncFn1<'a,$arg1, $return_type2, $error_type2> = Box::new(r1);
225 r
226 }
227 }
228 }
229 }
230}
231macro_rules! impl_injector {
232 ([$($args:ident),*], $provided:ident, $return_type:ident, $error_type:ident, $arg_size:literal, $return_fn_arg_size:literal) => {
233
234 paste! {
235 #[doc = concat!("dependency injection function provide_f", stringify!($arg_size), " for injecting the last argument of a given sync function")]
236 pub fn [<provider_f $arg_size>]<'a, $($args),*, $provided, $return_type, $error_type>(fn1: [<BoxedFn $arg_size>]<'a, $($args),*, $provided, $return_type, $error_type>,provided_data: $provided,) -> [<BoxedFn $return_fn_arg_size>]<'a, $($args),* , $return_type, $error_type> where $( $args: 'a ),*, $provided: Send + Sync + 'a, $return_type: 'a, $error_type: 'a{
237 Box::new(move |$( [<$args:lower>]:$args ),*| fn1($( [<$args:lower>]),*, provided_data))
238 }
239
240 #[doc = concat!("dependency injection function provider_async_f", stringify!($arg_size), " for injecting the last argument of a given async function")]
241 pub fn [<provider_async_f $arg_size>]<'a, $($args),*, $provided, $return_type, $error_type>(fn1: [<BoxedAsyncFn $arg_size>]<'a, $($args),*, $provided, $return_type, $error_type>,provided_data: $provided,) -> [<BoxedAsyncFn $return_fn_arg_size>]<'a, $($args),* , $return_type, $error_type> where $( $args: 'a ),*, $provided: Send + Sync + 'a, $return_type: 'a, $error_type: 'a{
242 Box::new(move |$( [<$args:lower>]:$args ),*| fn1($( [<$args:lower>]),*, provided_data))
243 }
244
245 }
246 paste!{
247
248 #[doc = concat!("Injector implementation for a given sync function that accepts " , stringify!($return_fn_arg_size+1), " arguments and returns a function with ", stringify!($return_fn_arg_size), " arguments")]
249 impl<'a, $($args),*, $provided, $return_type, $error_type> Injector<$provided, [<BoxedFn $return_fn_arg_size>]<'a, $($args),*, $return_type, $error_type>> for [<BoxedFn $arg_size>] <'a, $($args),*, $provided, $return_type, $error_type>
250 where $( $args: 'a ),*, $provided: Send + Sync +'a, $return_type: 'a, $error_type: 'a
251 {
252 fn provide(self, a: $provided) -> [<BoxedFn $return_fn_arg_size>]<'a, $($args),*, $return_type, $error_type> {
253 let r = [<provider_f $arg_size>](self, a);
254 r
255 }
256 }
257
258 #[doc = concat!("Injector implementation for a given async function that accepts " , stringify!($return_fn_arg_size+1), " arguments and returns a function with ", stringify!($return_fn_arg_size), " arguments")]
259 impl<'a, $($args),*, $provided, $return_type, $error_type> Injector<$provided, [<BoxedAsyncFn $return_fn_arg_size>]<'a, $($args),*, $return_type, $error_type>> for [<BoxedAsyncFn $arg_size>] <'a, $($args),*, $provided, $return_type, $error_type>
260 where $( $args: 'a ),*, $provided: Send + Sync +'a, $return_type: 'a, $error_type: 'a
261 {
262 fn provide(self, a: $provided) -> [<BoxedAsyncFn $return_fn_arg_size>]<'a, $($args),*, $return_type, $error_type> {
263 let r = [<provider_async_f $arg_size>](self, a);
264 r
265 }
266 }
267 }
268 };
269}
270
271macro_rules! generate_boxed_fn {
272 ( [$($args:ident),*], $return_type:ident,$error_type:ident, $arg_size:expr ) => {
273
274 crate::concat_idents!(boxed_fn_name = BoxedFn,$arg_size {
276 #[doc = concat!("Type alias BoxedFn", stringify!($arg_size), " for Boxed FnOnce sync function with ", stringify!($arg_size), " arguments")]
277 pub type boxed_fn_name<'a, $($args),*, $return_type, $error_type> = Box<dyn FnOnce($($args),*) -> Result<$return_type, $error_type> + Send + Sync + 'a>;
278 });
279
280 crate::concat_idents!(boxed_fn_name = BoxedAsyncFn,$arg_size {
281 #[doc = concat!("Type alias BoxedAsyncFn", stringify!($arg_size), " for Boxed FnOnce async function" , stringify!($arg_size), " arguments")]
282 pub type boxed_fn_name<'a, $($args),*, $return_type,$error_type> = Box<dyn FnOnce($($args),*) -> BoxFuture<'a, Result<$return_type, $error_type>> + Send + Sync + 'a>;
283 });
284
285 paste!{
286 #[doc = concat!("Function to box FnOnce sync function with ", stringify!($arg_size), " aguments and coerce it to BoxedFn",stringify!($arg_size))]
287 pub fn [<lift_sync_fn $arg_size>]<'a, $($args),*, $return_type, $error_type, F: FnOnce($($args),*) -> Result<$return_type, $error_type> + Send + Sync + 'a>(f: F,) -> [<BoxedFn $arg_size>]<'a, $($args),*, $return_type, $error_type> {
288 Box::new(f)
289 }
290
291 #[doc = concat!("Function to box FnOnce sync function with ", stringify!($arg_size), " aguments and coerce it to BoxedAsyncFn",stringify!($arg_size))]
292 pub fn [<lift_async_fn $arg_size>]<'a, $($args),*, $return_type, $error_type, F: FnOnce($($args),*) -> BoxFuture<'a,Result<$return_type, $error_type>> + Send + Sync + 'a>(f: F,) -> [<BoxedAsyncFn $arg_size>]<'a, $($args),*, $return_type, $error_type> {
293 Box::new(f)
294 }
295 }
296 }
297}
298
299generate_boxed_fn! {[T1], T2, E1, 1}
300
301generate_boxed_fn!([T1, T2], T3, E1, 2);
302impl_injector! {[T1],T2, T3, E1, 2, 1}
303
304generate_boxed_fn!([T1, T2, T3], T4, E1, 3);
305impl_injector!([T1, T2], T3, T4, E1, 3, 2);
306
307generate_boxed_fn!([T1, T2, T3, T4], T5, E1, 4);
308impl_injector!([T1, T2, T3], T4, T5, E1, 4, 3);
309
310generate_boxed_fn!([T1, T2, T3, T4, T5], T6, E1, 5);
311impl_injector!([T1, T2, T3, T4], T5, T6, E1, 5, 4);
312
313generate_boxed_fn!([T1, T2, T3, T4, T5, T6], T7, E1, 6);
314impl_injector!([T1, T2, T3, T4, T5], T6, T7, E1, 6, 5);
315
316generate_boxed_fn!([T1, T2, T3, T4, T5, T6, T7], T8, E1, 7);
317impl_injector!([T1, T2, T3, T4, T5, T6], T7, T8,E1, 7, 6);
318
319generate_boxed_fn!([T1, T2, T3, T4, T5, T6, T7, T8], T9, E1, 8);
320impl_injector!([T1, T2, T3, T4, T5, T6, T7], T8, T9, E1, 8, 7);
321
322composer_generator!(T1, T2, T3, E1, E2);
336
337
338
339
340
341pub trait Injector<I, O> {
342 fn provide(self, a: I) -> O;
343}
344
345pub trait Then<'a, A, B, C, F, R> {
355
356 fn then(self, f: F) -> R;
364}
365
366#[macro_use]
367pub mod macros {
368
369
370 #[macro_export]
371 macro_rules! compose {
372 ($fnLeft:ident,$is_left_fn_async:ident,-> with_args($args:expr) $($others:tt)*) => {
373 {
374 let r = $fnLeft($args);
375 r
376 }
377 };
378
379 ($fnLeft:ident,$is_left_fn_async:ident,.provide($p1:expr) $($others:tt)*) => {
380 {
381 use function_compose::Injector;
382 let p = $fnLeft.provide($p1);
383 let p1 = compose!(p,$is_left_fn_async,$($others)*);
384 p1
385 }
386 };
387
388 ($f_left:ident,$is_left_fn_async:ident,$f_right:ident, $isRightAsync:ident, .provide($p:expr) $($others:tt)*) =>{
389 {
390 let f_right = $f_right.provide($p);
391 let f3 = compose!($f_left,$is_left_fn_async,f_right,$isRightAsync,$($others)*);
392 f3
393 }
394 };
395
396 ($f_left:ident,$is_left_fn_async:ident,$f_right:ident, $isRightAsync:ident, -> $($others:tt)*) =>{
397 {
398 let f_left = $f_left.then($f_right);
399 let is_left_fn_async = $isRightAsync || $is_left_fn_async;
400 let f3 = compose!(f_left,is_left_fn_async, -> $($others)*);
401 f3
402 }
403 };
404
405 ($f_left:ident,$is_left_fn_async:ident,-> $fn:ident.provide($p:expr) $($others:tt)*) =>{
406 {
407 let f4;
408
409 crate::concat_idents!(lifted_fn_name = fn_composer__lifted_fn,_, $fn {
410 paste!{
411 let is_retryable = [< fn_composer__is_retryable_ $fn >]();
412 let current_f = if !is_retryable{
413 [<fn_composer__lifted_fn_ $fn>]($fn)
414 }else {
415 [<fn_composer__lifted_fn_ $fn>]([< fn_composer__ retry_ $fn>])
416 };
417 }
418 crate::concat_idents!(asyn_check_fn = fn_composer__is_async_, $fn {
419 let is_right_async = asyn_check_fn();
420 let f_right = current_f.provide($p);
421 let f3 = compose!($f_left,$is_left_fn_async,f_right,is_right_async,$($others)*);
422 f4 = f3;
423 });
424 });
425 f4
426 }
427 };
428
429 ($f_left:ident,$isLeftFnAsync:ident,-> $fn:ident.provide($p:expr) $($others:tt)*) =>{
430 {
431 let f4;
432 crate::concat_idents!(lifted_fn_name = fn_composer__lifted_fn,_, $fn {
433 let is_retryable = [<fn_composer__is_retryable $fn>]();
434 let current_f = if !is_retryable{
435 [<lifted_fn_name $fn>]($fn)
436 }else {
437 [<lifted_fn_name $fn>]([<fn_composer__ retry_ $fn>])
438 };
439 crate::concat_idents!(asyn_check_fn = fn_composer__is_async_, $fn {
440 let isRightAsync = asyn_check_fn();
441 let f_right = current_f.provide($p);
442 let f3 = compose!($f_left,$is_left_fn_async,f_right,isRightAsync,$($others)*);
443 f4 = f3;
444 });
445 });
446 f4
447 }
448 };
449
450
451 ($f_left:ident,$is_left_fn_async:ident,-> $fn:ident $($others:tt)*) =>{
452 {
453 let f4;
454 paste!{
455
456 let asyn_check_fn = [<fn_composer__is_async_ $fn>];
457 let current_async = asyn_check_fn();
458 let _is_result_async = current_async || $is_left_fn_async;
459 let is_retryable = [<fn_composer__is_retryable_ $fn>]();
460 let current_f = if !is_retryable{
461 [<fn_composer__lifted_fn_ $fn>]($fn)
462 }else {
463 [<fn_composer__lifted_fn_ $fn>]([<fn_composer__ retry_ $fn>])
464 };
465 let f3 = $f_left.then(current_f);
466 let f3 = compose!(f3,_is_result_async,$($others)*);
467 f4 = f3;
468 }
469 f4
470 }
471 };
472
473
474
475 ($fn:ident $($others:tt)*) => {
476 {
477
478 use Then;
479 let f2;
480 paste!{
481 let f = [<fn_composer__lifted_fn_ $fn>]($fn);
482 let is_async = [<fn_composer__is_async_ $fn>]();
483 let is_retryable = [<fn_composer__is_retryable_ $fn>]();
484 let f = if !is_retryable{
485 [<fn_composer__lifted_fn_ $fn>]($fn)
486 }else {
487 [<fn_composer__lifted_fn_ $fn>]([<fn_composer__ retry_ $fn>])
488 };
489 let f1 = compose!(f,is_async,$($others)*);
490 f2 = f1;
491 };
492 f2
493 }
494 };
495
496
497 }
498}
499