1use 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 }.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 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
262composer_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
285pub trait Then<'a, A, B, C, F, R> {
295
296 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