function_compose/
lib.rs

1//un comment below lines to debug macros
2
3/*#![feature(trace_macros)]
4trace_macros!(true);*/
5//! Crate `function-compose` provides utilities for composing functions and way to inject arguments to functions
6//!
7//!
8//! ### Usage
9//! ```rust
10//! use function_compose::composeable;
11//! #[composeable()]
12//! pub fn add_10(a: i32) -> Result<i32, String> {
13//!     Ok(a + 10)
14//! }
15//! 
16//! ```
17//! 
18//! ##### The async function should return BoxFuture.
19//! 
20//! ```rust
21//! use function_compose::composeable;
22//! use futures::{future::BoxFuture, FutureExt};
23//! #[composeable()]
24//! pub fn add_async(a: i32, b: i32) -> BoxFuture<'static, Result<i32, String>> {
25//!     async move {
26//!         let r = a + b;
27//!         Ok(r)
28//!     }.boxed()
29//! }
30//! ```
31//! 
32//! ##### Composing async and sync functions usage
33//!
34//!```ignore
35//! use function_compose::compose;
36//! use fn_macros::composeable;
37//! use futures::{future::BoxFuture, FutureExt};
38//! #[composeable()]
39//! pub fn add_async(a: i32, b: i32) -> BoxFuture<'static, Result<i32, String>> {
40//!     async move {
41//!         let r = a + b;
42//!         Ok(r)
43//!     }.boxed()
44//! }
45//! #[composeable()]
46//! pub fn add_10(a: i32) -> Result<i32, String> {
47//!     Ok(a + 10)
48//! }
49//! async fn test(){
50//!    let result = compose!(add_async.provide(10) -> add_100 -> with_args(10)).await;
51//!    assert_eq!(210, result.unwrap());
52//! }
53//! ```
54//! ##### Function argument injection usage
55//!```rust
56//! use function_compose::composeable;
57//! use futures::{future::BoxFuture, FutureExt};
58//! #[composeable()]
59//! pub fn add_3_arg_async(a: i32,b: i32, c:i32) -> BoxFuture<'static, Result<i32, String>>{
60//!     async move{
61//!         let  r =   a + b + c;
62//!         Ok(r)
63//!     }.boxed()
64//! }
65//! ```
66//! ##### Example of multiple injection to async function
67//!
68//!```ignore
69//! use crate::compose;
70//! let result = compose!(add_3_arg_async.provide(100).provide(200) -> add_10 -> with_args(10)).await;
71//! assert_eq!(220, result.unwrap());
72//!```
73
74
75use futures::{future::BoxFuture, FutureExt};
76
77
78
79
80
81
82fn to_fn_error<E1, E2>(error:E1) -> E2 where E2:From<E1>{
83    From::from(error)    
84}
85
86
87pub use function_compose_proc_macros::*;
88pub use paste::*;
89pub use concat_idents::concat_idents;
90
91macro_rules! composer_generator {
92    ($arg1:ident, $return_type1:ident, $return_type2:ident, $error_type1:ident, $error_type2:ident) => {
93        paste!{
94            #[doc = concat!("Then implementation for composing sync function (BoxedFn1) with another sync function(BoxedFn1) ")]
95            impl<'a, $arg1: 'a + Send, $return_type1: 'a + Send, $return_type2: 'a, $error_type1: Send + 'a, $error_type2: Send + 'a>
96                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>{
97
98                fn then(self, f: BoxedFn1<'a, $return_type1, $return_type2, $error_type2>) -> BoxedFn1<'a, $arg1, $return_type2, $error_type2> {
99                    let r1 = move |x: $arg1| {
100                        let g_result = self(x);
101                        match g_result{
102                                Ok(inner_result) => f(inner_result),
103                                Err(error) =>   Err(to_fn_error(error)),
104                            }
105                    };
106                    Box::new(r1)
107                }
108            }
109
110            #[doc = concat!("Then implementation for composing sync function(BoxedFn1) with another async function(BoxedAsyncFn1) ")]
111            impl<'a, $arg1: 'a + Send, $return_type1: 'a + Send, $return_type2: 'a, $error_type1: Send + 'a, $error_type2: Send + 'a>
112                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>{
113
114                fn then(self, f: BoxedAsyncFn1<'a, $return_type1, $return_type2, $error_type2>) -> BoxedAsyncFn1<'a, $arg1, $return_type2, $error_type2> {
115                    let r1 =  |x: $arg1| {
116                        async move{
117                            let g_result = self(x);
118                            match g_result{
119                                Ok(inner_result) => f(inner_result).await,
120                                Err(error) =>   Err(to_fn_error(error)),
121                            }
122                            //f(b).await
123                        }.boxed()
124                    };
125                    Box::new(r1)
126                }
127            }
128
129            #[doc = concat!("Then implementation for composing async function(BoxedAsyncFn1) with another sync function(BoxedFn1) ")]
130
131            impl<'a, $arg1: 'a + Send, $return_type1: 'a + Send, $return_type2: 'a, $error_type1:Send +  'a, $error_type2:Send +  'a>
132                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>{
133
134                fn then(self, f: BoxedFn1<'a, $return_type1, $return_type2, $error_type2>) -> BoxedAsyncFn1<'a, $arg1, $return_type2, $error_type2> {
135                    let r1 = |a: $arg1| {
136                        async move {
137                            let g_result = self(a).await;
138                            match g_result{
139                                Ok(inner_result) => f(inner_result),
140                                Err(error) =>   Err(to_fn_error(error)),
141                            }
142                        }.boxed()
143                    };
144                    let r: BoxedAsyncFn1<'a,$arg1, $return_type2, $error_type2> = Box::new(r1);
145                    r
146                }
147            }
148
149
150            #[doc = concat!("Then implementation for composing async function(BoxedAsyncFn1) with another async function(BoxedAsyncFn1) ")]
151            impl<'a, $arg1: 'a + Send, $return_type1: 'a + Send, $return_type2: 'a, $error_type1:Send +  'a, $error_type2:Send + 'a>
152                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>{
153
154                fn then(self, f: BoxedAsyncFn1<'a, $return_type1, $return_type2, $error_type2>) -> BoxedAsyncFn1<'a, $arg1, $return_type2, $error_type2> {
155                    let r1 = |a: $arg1| {
156                        async move {
157                            let g_result = self(a).await;
158                            match g_result{
159                                Ok(inner_result) => f(inner_result).await,
160                                Err(error) =>   Err(to_fn_error(error)),
161                            }
162                        }.boxed()
163                    };
164                    let r: BoxedAsyncFn1<'a,$arg1, $return_type2, $error_type2> = Box::new(r1);
165                    r
166                }
167            }
168        }
169    }
170}
171macro_rules! impl_injector {
172    ([$($args:ident),*], $provided:ident, $return_type:ident, $error_type:ident, $arg_size:literal, $return_fn_arg_size:literal) => {
173
174        paste!  {
175            #[doc = concat!("dependency injection function provide_f", stringify!($arg_size), " for injecting the last argument of a given sync function")]
176            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{
177                    Box::new(move |$( [<$args:lower>]:$args ),*| fn1($( [<$args:lower>]),*,  provided_data))
178            }
179
180            #[doc = concat!("dependency injection function provider_async_f", stringify!($arg_size), " for injecting the last argument of a given async function")]
181            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{
182                    Box::new(move |$( [<$args:lower>]:$args ),*| fn1($( [<$args:lower>]),*,  provided_data))
183            }
184
185        }
186        paste!{
187
188            #[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")]
189            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>
190            where $( $args: 'a ),*, $provided: Send + Sync +'a, $return_type: 'a, $error_type: 'a
191            {
192                fn provide(self, a: $provided) -> [<BoxedFn $return_fn_arg_size>]<'a, $($args),*, $return_type, $error_type> {
193                    let r = [<provider_f $arg_size>](self, a);
194                    r
195                }
196            }
197
198            #[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")]
199            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>
200            where $( $args: 'a ),*, $provided: Send + Sync +'a, $return_type: 'a, $error_type: 'a
201            {
202                fn provide(self, a: $provided) -> [<BoxedAsyncFn $return_fn_arg_size>]<'a, $($args),*, $return_type, $error_type> {
203                    let r = [<provider_async_f $arg_size>](self, a);
204                    r
205                }
206            }
207        }
208    };
209}
210
211macro_rules! generate_boxed_fn {
212    ( [$($args:ident),*], $return_type:ident,$error_type:ident, $arg_size:expr ) => {
213
214            //let x = count!($($args),*);
215            crate::concat_idents!(boxed_fn_name = BoxedFn,$arg_size  {
216                #[doc = concat!("Type alias  BoxedFn", stringify!($arg_size), "  for Boxed FnOnce sync function with ", stringify!($arg_size), " arguments")]
217                pub type boxed_fn_name<'a, $($args),*, $return_type, $error_type> = Box<dyn FnOnce($($args),*) -> Result<$return_type, $error_type> + Send + Sync + 'a>;
218            });
219
220            crate::concat_idents!(boxed_fn_name = BoxedAsyncFn,$arg_size  {
221                #[doc = concat!("Type alias  BoxedAsyncFn", stringify!($arg_size), "  for Boxed FnOnce async function" , stringify!($arg_size), " arguments")]
222                    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>;
223                });
224
225            paste!{
226                #[doc = concat!("Function to box FnOnce sync function with ", stringify!($arg_size), " aguments and coerce it to BoxedFn",stringify!($arg_size))]
227                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> {
228                    Box::new(f)
229                }
230
231                #[doc = concat!("Function to box  FnOnce sync function with ", stringify!($arg_size), " aguments and coerce it to BoxedAsyncFn",stringify!($arg_size))]
232                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> {
233                    Box::new(f)
234                }
235            }
236    }
237}
238
239generate_boxed_fn! {[T1], T2, E1, 1}
240
241generate_boxed_fn!([T1, T2], T3, E1,  2);
242impl_injector! {[T1],T2, T3, E1, 2, 1}
243
244generate_boxed_fn!([T1, T2, T3], T4, E1, 3);
245impl_injector!([T1, T2], T3, T4, E1, 3, 2);
246
247generate_boxed_fn!([T1, T2, T3, T4], T5, E1, 4);
248impl_injector!([T1, T2, T3], T4, T5, E1, 4, 3);
249
250generate_boxed_fn!([T1, T2, T3, T4, T5], T6, E1,  5);
251impl_injector!([T1, T2, T3, T4], T5, T6, E1, 5, 4);
252
253generate_boxed_fn!([T1, T2, T3, T4, T5, T6], T7, E1, 6);
254impl_injector!([T1, T2, T3, T4, T5], T6, T7, E1, 6, 5);
255
256generate_boxed_fn!([T1, T2, T3, T4, T5, T6, T7], T8, E1, 7);
257impl_injector!([T1, T2, T3, T4, T5, T6], T7, T8,E1, 7, 6);
258
259generate_boxed_fn!([T1, T2, T3, T4, T5, T6, T7, T8], T9, E1, 8);
260impl_injector!([T1, T2, T3, T4, T5, T6, T7], T8, T9, E1, 8, 7);
261
262//Generates a function composition for BoxedFn1 as below. The below is example of composing sync with sync function.
263//Similar code is generated for composing sync with async function, async with sync function and async with async function.
264// impl<'a, T1: 'a + Send, T2: 'a + Send, T3: 'a>
265// Then<'a, T1, T2, T3, BoxedFn1<'a, T2, T3>, BoxedFn1<'a, T1, T3>> for BoxedFn1<'a, T1, T2> {
266//     fn then(self, f: BoxedFn1<'a, T2, T3>) -> BoxedFn1<'a, T1, T3> {
267//         let r1 = move |x: T1| {
268//             let b = self(x)?;
269//             let r = f(b)?;
270//             Ok(r)
271//         };
272//         Box::new(r1)
273//     }
274// }
275composer_generator!(T1, T2, T3, E1, E2);
276
277
278
279
280
281pub trait Injector<I, O> {
282    fn provide(self, a: I) -> O;
283}
284
285///trait Then allows you to compose functions.
286/// Type param A represents the function arg of Self
287///
288/// Type param B represents the return type of Self
289///
290///Type B also acts as the input arg of function f
291///
292/// Type C represents the return type of  function f
293
294pub trait Then<'a, A, B, C, F, R> {
295
296    /// Compose self with function f
297    ///
298    /// self:function(A)->B
299    ///
300    /// f:function(B)->C
301    ///
302    /// returns function(A)->C
303    fn then(self, f: F) -> R;
304}
305
306#[macro_use]
307pub mod macros {
308
309
310    #[macro_export]
311    macro_rules! compose {
312        ($fnLeft:ident,$isLeftFnAsync:ident,-> with_args($args:expr) $($others:tt)*) => {
313            {
314            let r = $fnLeft($args);
315            r
316            }
317        };
318
319        ($fnLeft:ident,$isLeftFnAsync:ident,.provide($p1:expr) $($others:tt)*) => {
320            {
321                use function_compose::Injector;
322                let p = $fnLeft.provide($p1);
323                let p1 = compose!(p,$isLeftFnAsync,$($others)*);
324                p1
325            }
326        };
327
328        ($fLeft:ident,$isLeftFnAsync:ident,$fRight:ident, $isRightAsync:ident,  .provide($p:expr) $($others:tt)*) =>{
329            {
330                let fRight = $fRight.provide($p);
331                let f3 = compose!($fLeft,$isLeftFnAsync,fRight,$isRightAsync,$($others)*);
332                f3
333            }
334        };
335
336        ($fLeft:ident,$isLeftFnAsync:ident,$fRight:ident, $isRightAsync:ident,  ->  $($others:tt)*) =>{
337            {
338                let fLeft = $fLeft.then($fRight);
339                let isLeftFnAsync = $isRightAsync || $isLeftFnAsync;
340                let f3 = compose!(fLeft,isLeftFnAsync, -> $($others)*);
341                f3
342            }
343        };
344
345        ($fLeft:ident,$isLeftFnAsync:ident,-> $fn:ident.provide($p:expr) $($others:tt)*) =>{
346            {
347                let f4;
348                
349                crate::concat_idents!(lifted_fn_name = fn_composer__lifted_fn,_, $fn {
350                    paste!{
351                    let is_retryable = [< fn_composer__is_retryable_ $fn >]();
352                    let current_f = if !is_retryable{
353                        [<fn_composer__lifted_fn_ $fn>]($fn)
354                    }else {
355                        [<fn_composer__lifted_fn_ $fn>]([< fn_composer__ retry_ $fn>])
356                    };
357                    }
358                    crate::concat_idents!(asynCheckFn = fn_composer__is_async_, $fn {
359                        let isRightAsync = asynCheckFn();
360                        let fRight = current_f.provide($p);
361                        let f3 = compose!($fLeft,$isLeftFnAsync,fRight,isRightAsync,$($others)*);
362                        f4 = f3;
363                    });
364                });
365                f4
366            }
367        };
368
369        ($fLeft:ident,$isLeftFnAsync:ident,-> $fn:ident.provide($p:expr) $($others:tt)*) =>{
370            {
371                let f4;
372                crate::concat_idents!(lifted_fn_name = fn_composer__lifted_fn,_, $fn {
373                    let is_retryable = [<fn_composer__is_retryable $fn>]();
374                    let current_f = if !is_retryable{
375                        [<lifted_fn_name $fn>]($fn)
376                    }else {
377                        [<lifted_fn_name $fn>]([<fn_composer__ retry_ $fn>])
378                    };
379                    crate::concat_idents!(asynCheckFn = fn_composer__is_async_, $fn {
380                        let isRightAsync = asynCheckFn();
381                        let fRight = current_f.provide($p);
382                        let f3 = compose!($fLeft,$isLeftFnAsync,fRight,isRightAsync,$($others)*);
383                        f4 = f3;
384                    });
385                });
386                f4
387            }
388        };
389
390
391        ($fLeft:ident,$isLeftFnAsync:ident,-> $fn:ident $($others:tt)*) =>{
392            {
393                let f4;
394                paste!{
395
396                    let asynCheckFn = [<fn_composer__is_async_ $fn>];
397                    let currentAsync = asynCheckFn();
398                    let _isResultAsync = currentAsync || $isLeftFnAsync;
399                    let is_retryable = [<fn_composer__is_retryable_ $fn>]();
400                    let current_f = if !is_retryable{
401                        [<fn_composer__lifted_fn_ $fn>]($fn)
402                    }else {
403                        [<fn_composer__lifted_fn_ $fn>]([<fn_composer__ retry_ $fn>])
404                    };
405                    let f3 = $fLeft.then(current_f);
406                    let f3 = compose!(f3,_isResultAsync,$($others)*);
407                    f4 = f3;
408                }
409                f4
410            }
411        };
412
413
414
415        ($fn:ident $($others:tt)*) => {
416            {
417
418                use Then;
419                let f2;
420                paste!{
421                    let f = [<fn_composer__lifted_fn_ $fn>]($fn);
422                    let isAsync = [<fn_composer__is_async_ $fn>]();
423                    let is_retryable = [<fn_composer__is_retryable_ $fn>]();
424                    let f = if !is_retryable{
425                        [<fn_composer__lifted_fn_ $fn>]($fn)
426                    }else {
427                        [<fn_composer__lifted_fn_ $fn>]([<fn_composer__ retry_ $fn>])
428                    };
429                    let f1 = compose!(f,isAsync,$($others)*);
430                    f2 = f1;
431                };
432                f2
433            }
434        };
435
436
437    }
438}
439