From 1b0acfee2b32c75141ade4e2c1cb9a2a9b5e8777 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 16:50:37 +0000 Subject: [PATCH 1/6] Create draft PR for #20 [skip ci] From eb68e4d588be289baa966244aead0c8f7ad86225 Mon Sep 17 00:00:00 2001 From: Matt Edwards Date: Thu, 8 Aug 2024 12:24:47 -0400 Subject: [PATCH 2/6] WIP: convert binders to use expressions --- .../Binders/CallBlockBinder.cs | 40 +++++++++--- .../Binders/CallStatementBinder.cs | 50 +++++++++++---- .../Binders/ForEachBlockBinder.cs | 35 +++++++++-- src/Hyperbee.Pipeline/Binders/HookBinder.cs | 48 ++++++++++++-- .../Binders/PipeBlockBinder.cs | 60 +++++++++++++++++- .../Binders/PipeStatementBinder.cs | 58 +++++++++++++---- .../Binders/ReduceBlockBinder.cs | 39 +++++++++--- .../Binders/WaitAllBlockBinder.cs | 62 ++++++++++++++----- src/Hyperbee.Pipeline/Binders/WrapBinder.cs | 37 ++++++++--- .../Builders/PipeBlockBuilder.cs | 5 +- src/Hyperbee.Pipeline/IPipelineFunction.cs | 12 ++-- src/Hyperbee.Pipeline/PipelineBuilder.cs | 22 ++++--- src/Hyperbee.Pipeline/PipelineFactory.cs | 7 ++- 13 files changed, 386 insertions(+), 89 deletions(-) diff --git a/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs index cdbafe1..c6eadb5 100644 --- a/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs @@ -1,28 +1,52 @@ -namespace Hyperbee.Pipeline.Binders; +using System.Linq.Expressions; +using Hyperbee.Pipeline.Context; +using System.Reflection; + +namespace Hyperbee.Pipeline.Binders; internal class CallBlockBinder { - private FunctionAsync Pipeline { get; } - private Function Condition { get; } + private Expression> Pipeline { get; } + private Expression> Condition { get; } - public CallBlockBinder( FunctionAsync function ) + public CallBlockBinder( Expression> function ) : this( null, function ) { } - public CallBlockBinder( Function condition, FunctionAsync function ) + public CallBlockBinder( Expression> condition, Expression> function ) { Condition = condition; Pipeline = function; } + public Expression> Bind( Expression> next ) + { + // Get the MethodInfo for the BindImpl method + var bindImplMethodInfo = typeof( CallBlockBinder ) + .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic | BindingFlags.Static )! + .MakeGenericMethod( typeof( TInput ), typeof( TOutput ) ); + + // Create the call expression to BindImpl + var callBind = Expression.Call( + bindImplMethodInfo, + next, + Pipeline, + Condition + ); + + // Create and return the final expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); + return Expression.Lambda>( callBind, paramContext, paramArgument ); + } - public FunctionAsync Bind( FunctionAsync next ) + private static FunctionAsync BindImpl( FunctionAsync next, FunctionAsync pipeline, Function condition ) { return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); + var nextArgument = await pipeline( context, argument ).ConfigureAwait( false ); - if ( Condition == null || Condition( context, nextArgument ) ) + if ( condition == null || condition( context, nextArgument ) ) await next( context, nextArgument ).ConfigureAwait( false ); return nextArgument; diff --git a/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs b/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs index 36ad071..4800629 100644 --- a/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.Linq.Expressions; +using System.Reflection; using Hyperbee.Pipeline.Context; using Hyperbee.Pipeline.Extensions.Implementation; @@ -6,38 +7,65 @@ namespace Hyperbee.Pipeline.Binders; internal class CallStatementBinder { - private FunctionAsync Pipeline { get; } - private MiddlewareAsync Middleware { get; } - private Action Configure { get; } + private Expression> Pipeline { get; } + private Expression> Middleware { get; } + private Expression> Configure { get; } - public CallStatementBinder( FunctionAsync function, MiddlewareAsync middleware, Action configure ) + public CallStatementBinder( Expression> function, Expression> middleware, Expression> configure ) { Pipeline = function; Middleware = middleware; Configure = configure; } - public FunctionAsync Bind( ProcedureAsync next, MethodInfo method = null ) + public Expression> Bind( Expression> next, MethodInfo method = null ) + { + // Get the MethodInfo for the BindImpl method + var bindImplMethodInfo = typeof( CallStatementBinder ) + .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic | BindingFlags.Static )! + .MakeGenericMethod( typeof( TInput ), typeof( TOutput ) ); + + // Create the call expression to BindImpl + var callBind = Expression.Call( + bindImplMethodInfo, + next, + Pipeline, + Configure, + Expression.Constant( method, typeof( MethodInfo ) ) + ); + + // Create and return the final expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); + return Expression.Lambda>(callBind, paramContext, paramArgument); + } + + public FunctionAsync BindImpl( + ProcedureAsync next, + FunctionAsync pipeline, + MiddlewareAsync middleware, + Action configure, + MethodInfo method = null ) { var defaultName = (method ?? next.Method).Name; return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); + var nextArgument = await pipeline( context, argument ).ConfigureAwait( false ); var contextControl = (IPipelineContextControl) context; if ( contextControl.HandleCancellationRequested( nextArgument ) ) return default; - using ( contextControl.CreateFrame( context, Configure, defaultName ) ) + using ( contextControl.CreateFrame( context, configure, defaultName ) ) { - return await Next( next, context, nextArgument ).ConfigureAwait( false ); + return await Next( next, middleware, context, nextArgument ).ConfigureAwait( false ); } }; } - private async Task Next( ProcedureAsync next, IPipelineContext context, TOutput nextArgument ) + private async Task Next( ProcedureAsync next, MiddlewareAsync middleware, IPipelineContext context, TOutput nextArgument ) { if ( Middleware == null ) { @@ -45,7 +73,7 @@ private async Task Next( ProcedureAsync next, IPipelineContext return nextArgument; } - await Middleware( + await middleware( context, nextArgument, async ( context1, argument1 ) => diff --git a/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs index f29fcb0..2e36961 100644 --- a/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs @@ -1,19 +1,44 @@ -namespace Hyperbee.Pipeline.Binders; +using System.Linq.Expressions; +using Hyperbee.Pipeline.Context; +using System.Reflection; + +namespace Hyperbee.Pipeline.Binders; internal class ForEachBlockBinder { - private FunctionAsync Pipeline { get; } + private Expression> Pipeline { get; } - public ForEachBlockBinder( FunctionAsync function ) + public ForEachBlockBinder( Expression> function ) { Pipeline = function; } - public FunctionAsync Bind( FunctionAsync next ) + + public Expression> Bind( Expression> next ) + { + // Get the MethodInfo for the BindImpl method + var bindImplMethodInfo = typeof( ForEachBlockBinder ) + .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic | BindingFlags.Static )! + .MakeGenericMethod( typeof( TInput ), typeof( TOutput ) ); + + // Create the call expression to BindImpl + var callBind = Expression.Call( + bindImplMethodInfo, + next, + Pipeline + ); + + // Create and return the final expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); + return Expression.Lambda>( callBind, paramContext, paramArgument ); + } + + private static FunctionAsync BindImpl( FunctionAsync next, FunctionAsync pipeline ) { return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); + var nextArgument = await pipeline( context, argument ).ConfigureAwait( false ); var nextArguments = (IEnumerable) nextArgument; foreach ( var elementArgument in nextArguments ) diff --git a/src/Hyperbee.Pipeline/Binders/HookBinder.cs b/src/Hyperbee.Pipeline/Binders/HookBinder.cs index c3be299..b1daee3 100644 --- a/src/Hyperbee.Pipeline/Binders/HookBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/HookBinder.cs @@ -1,21 +1,57 @@ -namespace Hyperbee.Pipeline.Binders; +using System.Linq.Expressions; +using Hyperbee.Pipeline.Context; +using System.Reflection; + +namespace Hyperbee.Pipeline.Binders; internal class HookBinder // explicit Type Args due to usage { - private MiddlewareAsync Middleware { get; } + private Expression> Middleware { get; } + + public HookBinder( Expression> middleware ) + { + if ( middleware == null ) + { + // Create and return the final expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput1 ), "argument" ); + Middleware = Expression.Lambda>( + null + // TODO: empty middleware async ( context, argument, next ) => await next( context, argument ).ConfigureAwait( false ) + , paramContext, paramArgument ); + } + + this.Middleware = middleware; - public HookBinder( MiddlewareAsync middleware ) + } + + public Expression> Bind( MiddlewareAsync middleware ) { - Middleware = middleware ?? (async ( context, argument, next ) => await next( context, argument ).ConfigureAwait( false )); + // Get the MethodInfo for the BindImpl method + var bindImplMethodInfo = typeof( HookBinder ) + .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic | BindingFlags.Static )! + .MakeGenericMethod( typeof( TInput1 ), typeof( TOutput1 ) ); + + // Create the call expression to BindImpl + var callBind = Expression.Call( + bindImplMethodInfo, + Expression.Constant( middleware, typeof( MiddlewareAsync ) ), + Middleware + ); + + // Create and return the final expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput1 ), "argument" ); + return Expression.Lambda>( callBind, paramContext, paramArgument ); } - public MiddlewareAsync Bind( MiddlewareAsync middleware ) + private static MiddlewareAsync BindImpl( MiddlewareAsync middleware, MiddlewareAsync inner ) { return async ( context, argument, function ) => await middleware( context, argument, - async ( context1, argument1 ) => await Middleware( context1, argument1, function ).ConfigureAwait( false ) + async ( context1, argument1 ) => await inner( context1, argument1, function ).ConfigureAwait( false ) ).ConfigureAwait( false ); } } diff --git a/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs index d5acfe7..4290087 100644 --- a/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs @@ -1,4 +1,61 @@ -namespace Hyperbee.Pipeline.Binders; +using System.Linq.Expressions; +using System.Reflection; + +namespace Hyperbee.Pipeline.Binders; + +internal class PipeBlockBinder +{ + private Expression> Pipeline { get; } + private Expression> Condition { get; } + + public PipeBlockBinder( Expression> function ) + : this( null, function ) + { + } + + public PipeBlockBinder( Function condition, Expression> function ) + { + Condition = ConvertCondition( condition ); + Pipeline = function; + } + + public Expression> Bind( Expression> next ) + { + var paramContext = Expression.Parameter( typeof( TInput ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); + + var invokePipeline = Expression.Invoke( Pipeline, paramContext, paramArgument ); + var invokeCondition = Condition != null + ? Expression.Invoke( Condition, invokePipeline ) + : (Expression) Expression.Constant( true ); + + var invokeNext = Expression.Invoke( next, paramContext, invokePipeline ); + + var body = Expression.Condition( invokeCondition, invokeNext, Expression.Convert( invokePipeline, typeof( TNext ) ) ); + + return Expression.Lambda>( body, paramContext, paramArgument ); + } + + internal static Expression> ConvertCondition( Function del ) + { + // Get the MethodInfo of the delegate + var methodInfo = del.GetMethodInfo(); + + // Create a parameter expression + var parameter = Expression.Parameter( typeof( TInput ), "input" ); + + // Create a method call expression + var methodCall = Expression.Call( Expression.Constant( del.Target ), methodInfo, parameter ); + + // Create and return the lambda expression + return Expression.Lambda>( methodCall, parameter ); + } + + +} + + +/* internal class PipeBlockBinder { @@ -29,3 +86,4 @@ public FunctionAsync Bind( FunctionAsync n }; } } +*/ diff --git a/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs b/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs index 228acda..ea04523 100644 --- a/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.Linq.Expressions; +using System.Reflection; using Hyperbee.Pipeline.Context; using Hyperbee.Pipeline.Extensions.Implementation; @@ -6,43 +7,76 @@ namespace Hyperbee.Pipeline.Binders; internal class PipeStatementBinder { - private FunctionAsync Pipeline { get; } - private MiddlewareAsync Middleware { get; } - private Action Configure { get; } + private Expression> Pipeline { get; } + private Expression> Middleware { get; } + private Expression> Configure { get; } - public PipeStatementBinder( FunctionAsync function, MiddlewareAsync middleware, Action configure ) + public PipeStatementBinder( Expression> function, Expression> middleware, Expression> configure ) { Pipeline = function; Middleware = middleware; Configure = configure; } - public FunctionAsync Bind( FunctionAsync next, MethodInfo method = null ) + public Expression> Bind( Expression> next, MethodInfo method = null ) + { + // Get the MethodInfo for the BindImpl method + var bindImplMethodInfo = typeof( PipeStatementBinder ) + .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic | BindingFlags.Static )! + .MakeGenericMethod( typeof( TInput ), typeof( TOutput ), typeof( TNext ) ); + + // Create the call expression to BindImpl + var callBind = Expression.Call( + bindImplMethodInfo, + next, + Pipeline, + Middleware, + Configure, + Expression.Constant( method, typeof( MethodInfo ) ) + ); + + // Create and return the final expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); + return Expression.Lambda>( callBind, paramContext, paramArgument ); + + } + + private static FunctionAsync BindImpl( + FunctionAsync next, + FunctionAsync pipeline, + MiddlewareAsync middleware, + Action configure, + MethodInfo method = null ) { var defaultName = (method ?? next.Method).Name; return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); + var nextArgument = await pipeline( context, argument ).ConfigureAwait( false ); var contextControl = (IPipelineContextControl) context; if ( contextControl.HandleCancellationRequested( nextArgument ) ) return default; - using ( contextControl.CreateFrame( context, Configure, defaultName ) ) + using ( contextControl.CreateFrame( context, configure, defaultName ) ) { - return await Next( next, context, nextArgument ).ConfigureAwait( false ); + return await Next( next, middleware, context, nextArgument ).ConfigureAwait( false ); } }; } - private async Task Next( FunctionAsync next, IPipelineContext context, TOutput nextArgument ) + private static async Task Next( + FunctionAsync next, + MiddlewareAsync middleware, + IPipelineContext context, + TOutput nextArgument ) { - if ( Middleware == null ) + if ( middleware == null ) return await next( context, nextArgument ).ConfigureAwait( false ); - return (TNext) await Middleware( + return (TNext) await middleware( context, nextArgument, async ( context1, argument1 ) => await next( context1, (TOutput) argument1 ).ConfigureAwait( false ) diff --git a/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs index 0dbec9f..54f0400 100644 --- a/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs @@ -1,21 +1,46 @@ -namespace Hyperbee.Pipeline.Binders; +using System.Linq.Expressions; +using Hyperbee.Pipeline.Context; +using System.Reflection; + +namespace Hyperbee.Pipeline.Binders; internal class ReduceBlockBinder { - private FunctionAsync Pipeline { get; } - private Func Reducer { get; } + private Expression> Pipeline { get; } + private Expression> Reducer { get; } - public ReduceBlockBinder( Func reducer, FunctionAsync function ) + public ReduceBlockBinder( Expression> reducer, Expression> function ) { Reducer = reducer; Pipeline = function; } - public FunctionAsync Bind( FunctionAsync next ) + public Expression> Bind( Expression> next ) + { + // Get the MethodInfo for the BindImpl method + var bindImplMethodInfo = typeof( ReduceBlockBinder ) + .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic | BindingFlags.Static )! + .MakeGenericMethod( typeof( TInput ), typeof( TOutput ), typeof( TNext ) ); + + // Create the call expression to BindImpl + var callBind = Expression.Call( + bindImplMethodInfo, + next, + Pipeline, + Expression.Constant( Reducer ) + ); + + // Create and return the final expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); + return Expression.Lambda>( callBind, paramContext, paramArgument ); + } + + private static FunctionAsync BindImpl( FunctionAsync next, FunctionAsync pipeline, Func reducer ) { return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); + var nextArgument = await pipeline( context, argument ).ConfigureAwait( false ); var nextArguments = (IEnumerable) nextArgument; var accumulator = default( TNext ); @@ -23,7 +48,7 @@ public FunctionAsync Bind( FunctionAsync next ) foreach ( var elementArgument in nextArguments ) { var result = await next( context, elementArgument ).ConfigureAwait( false ); - accumulator = Reducer( accumulator, result ); + accumulator = reducer( accumulator, result ); } return accumulator; diff --git a/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs index 28dcd4b..15a3d06 100644 --- a/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs @@ -1,4 +1,6 @@ -using Hyperbee.Pipeline.Context; +using System.Linq.Expressions; +using System.Reflection; +using Hyperbee.Pipeline.Context; using Hyperbee.Pipeline.Extensions; using Hyperbee.Pipeline.Extensions.Implementation; @@ -6,17 +8,17 @@ namespace Hyperbee.Pipeline.Binders; internal class WaitAllBlockBinder { - private FunctionAsync Pipeline { get; } - private MiddlewareAsync Middleware { get; } - private Action Configure { get; } - private Function Condition { get; } + private Expression> Pipeline { get; } + private Expression> Middleware { get; } + private Expression> Configure { get; } + private Expression> Condition { get; } - public WaitAllBlockBinder( FunctionAsync function, MiddlewareAsync middleware, Action configure ) + public WaitAllBlockBinder( Expression> function, Expression> middleware, Expression> configure ) : this( null, function, middleware, configure ) { } - public WaitAllBlockBinder( Function condition, FunctionAsync function, MiddlewareAsync middleware, Action configure ) + public WaitAllBlockBinder( Expression> condition, Expression> function, Expression> middleware, Expression> configure ) { Condition = condition; Pipeline = function; @@ -24,15 +26,45 @@ public WaitAllBlockBinder( Function condition, FunctionAsync Bind( FunctionAsync[] nexts, WaitAllReducer reducer ) + public Expression> Bind( Expression[]> next, WaitAllReducer reducer ) + { + // Get the MethodInfo for the BindImpl method + var bindImplMethodInfo = typeof( WaitAllBlockBinder ) + .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic | BindingFlags.Static )! + .MakeGenericMethod( typeof( TInput ), typeof( TOutput ), typeof( TNext ) ); + + // Create the call expression to BindImpl + var callBind = Expression.Call( + bindImplMethodInfo, + next, + Pipeline, + Middleware, + Configure, + Condition, + Expression.Constant( reducer ) + ); + + // Create and return the final expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); + return Expression.Lambda>( callBind, paramContext, paramArgument ); + } + + public static FunctionAsync BindImpl( + FunctionAsync[] nexts, + FunctionAsync pipeline, + MiddlewareAsync middleware, + Action configure, + Function condition, + WaitAllReducer reducer ) { ArgumentNullException.ThrowIfNull( reducer ); return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); + var nextArgument = await pipeline( context, argument ).ConfigureAwait( false ); - if ( Condition != null && !Condition( context, nextArgument ) ) + if ( condition != null && !condition( context, nextArgument ) ) return (TNext) (object) nextArgument; // mind cancellation and execute @@ -41,9 +73,9 @@ public FunctionAsync Bind( FunctionAsync[ if ( contextControl.HandleCancellationRequested( nextArgument ) ) return default; - using ( contextControl.CreateFrame( context, Configure, nameof( WaitAllAsync ) ) ) + using ( contextControl.CreateFrame( context, configure, nameof( WaitAllAsync ) ) ) { - return await Next( WaitAllAsync, context, nextArgument ).ConfigureAwait( false ); + return await Next( WaitAllAsync, middleware, context, nextArgument ).ConfigureAwait( false ); } // WaitAllBlockBinder is unique in that it is both a block configure and a step. @@ -71,12 +103,12 @@ await items.ForEachAsync( async item => }; } - private async Task Next( FunctionAsync waitAll, IPipelineContext context, TOutput nextArgument ) + private static async Task Next( FunctionAsync waitAll, MiddlewareAsync middleware, IPipelineContext context, TOutput nextArgument ) { - if ( Middleware == null ) + if ( middleware == null ) return await waitAll( context, nextArgument ).ConfigureAwait( false ); - return (TNext) await Middleware( + return (TNext) await middleware( context, nextArgument, async ( context1, argument1 ) => await waitAll( context1, (TOutput) argument1 ).ConfigureAwait( false ) diff --git a/src/Hyperbee.Pipeline/Binders/WrapBinder.cs b/src/Hyperbee.Pipeline/Binders/WrapBinder.cs index c5c1af8..c35ac9b 100644 --- a/src/Hyperbee.Pipeline/Binders/WrapBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/WrapBinder.cs @@ -1,20 +1,43 @@ -using Hyperbee.Pipeline.Context; +using System.Linq.Expressions; +using System.Reflection; +using Hyperbee.Pipeline.Context; using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline.Binders; internal class WrapBinder { - private MiddlewareAsync Middleware { get; } - private Action Configure { get; } + private Expression> Middleware { get; } + private Expression> Configure { get; } - public WrapBinder( MiddlewareAsync middleware, Action configure ) + public WrapBinder( Expression> middleware, Expression> configure ) { Middleware = middleware; Configure = configure; } - public FunctionAsync Bind( FunctionAsync next ) + public Expression> Bind( Expression> next ) + { + // Get the MethodInfo for the BindImpl method + var bindImplMethodInfo = typeof( WrapBinder ) + .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic | BindingFlags.Static )! + .MakeGenericMethod( typeof( TInput ), typeof( TOutput ) ); + + // Create the call expression to BindImpl + var callBind = Expression.Call( + bindImplMethodInfo, + next, + Middleware, + Configure + ); + + // Create and return the final expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); + return Expression.Lambda>( callBind, paramContext, paramArgument ); + } + + private static FunctionAsync BindImpl( FunctionAsync next, MiddlewareAsync middleware, Action configure ) { var defaultName = next.Method.Name; @@ -22,9 +45,9 @@ public FunctionAsync Bind( FunctionAsync next { var contextControl = (IPipelineContextControl) context; - using ( contextControl.CreateFrame( context, Configure, defaultName ) ) + using ( contextControl.CreateFrame( context, configure, defaultName ) ) { - return await Middleware( + return await middleware( context, argument, async ( context1, argument1 ) => await next( context1, argument1 ).ConfigureAwait( false ) diff --git a/src/Hyperbee.Pipeline/Builders/PipeBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/PipeBlockBuilder.cs index f3f1153..2cf3561 100644 --- a/src/Hyperbee.Pipeline/Builders/PipeBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/PipeBlockBuilder.cs @@ -1,4 +1,6 @@ -using Hyperbee.Pipeline.Binders; +using System.Linq.Expressions; +using System.Reflection; +using Hyperbee.Pipeline.Binders; namespace Hyperbee.Pipeline; @@ -54,4 +56,5 @@ public IPipelineBuilder PipeIf( Function co Middleware = Middleware }; } + } diff --git a/src/Hyperbee.Pipeline/IPipelineFunction.cs b/src/Hyperbee.Pipeline/IPipelineFunction.cs index be6f538..90845fa 100644 --- a/src/Hyperbee.Pipeline/IPipelineFunction.cs +++ b/src/Hyperbee.Pipeline/IPipelineFunction.cs @@ -1,14 +1,16 @@ -namespace Hyperbee.Pipeline; +using System.Linq.Expressions; -public interface IPipelineFunctionProvider +namespace Hyperbee.Pipeline; + +public interface IPipelineFunctionProvider { // Provide access to the Function and the Middleware // so people can implement their own custom binders. IPipelineFunction GetPipelineFunction(); } -public interface IPipelineFunction +public interface IPipelineFunction { - FunctionAsync Function { get; } - MiddlewareAsync Middleware { get; } + Expression> Function { get; } + Expression> Middleware { get; } } diff --git a/src/Hyperbee.Pipeline/PipelineBuilder.cs b/src/Hyperbee.Pipeline/PipelineBuilder.cs index 1323464..e992ba9 100644 --- a/src/Hyperbee.Pipeline/PipelineBuilder.cs +++ b/src/Hyperbee.Pipeline/PipelineBuilder.cs @@ -1,11 +1,12 @@ -using Hyperbee.Pipeline.Data; +using System.Linq.Expressions; +using Hyperbee.Pipeline.Data; namespace Hyperbee.Pipeline; public partial class PipelineBuilder : PipelineFactory, IPipelineStartBuilder, IPipelineFunctionProvider { - internal FunctionAsync Function { get; init; } - internal MiddlewareAsync Middleware { get; init; } + internal Expression> Function { get; init; } + internal Expression> Middleware { get; init; } internal PipelineBuilder() { @@ -18,7 +19,8 @@ public FunctionAsync Build() { try { - var result = await Function( context, argument ).ConfigureAwait( false ); + var compiledPipeline = Function.Compile(); + var result = await compiledPipeline( context, argument ).ConfigureAwait( false ); if ( context.CancellationToken.IsCancellationRequested ) return Converter.TryConvertTo( context.CancellationValue, out var converted ) ? converted : default; @@ -44,7 +46,8 @@ public ProcedureAsync BuildAsProcedure() { try { - await Function( context, argument ).ConfigureAwait( false ); + var compiledPipeline = Function.Compile(); + await compiledPipeline( context, argument ).ConfigureAwait( false ); } catch ( Exception ex ) { @@ -60,7 +63,8 @@ FunctionAsync IPipelineBuilder.CastFunction() { return async ( context, argument ) => { - var result = await Function( context, Cast( argument ) ).ConfigureAwait( false ); + var compiledPipeline = Function.Compile(); + var result = await compiledPipeline( context, Cast( argument ) ).ConfigureAwait( false ); return Cast( result ); }; @@ -79,10 +83,10 @@ IPipelineFunction IPipelineFunctionProvider.Ge public record PipelineFunction : IPipelineFunction { - public FunctionAsync Function { get; init; } - public MiddlewareAsync Middleware { get; init; } + public Expression> Function { get; init; } + public Expression> Middleware { get; init; } - public void Deconstruct( out FunctionAsync function, out MiddlewareAsync middleware ) + public void Deconstruct( out Expression> function, out Expression> middleware ) { function = Function; middleware = Middleware; diff --git a/src/Hyperbee.Pipeline/PipelineFactory.cs b/src/Hyperbee.Pipeline/PipelineFactory.cs index 578dfd1..abc97c0 100644 --- a/src/Hyperbee.Pipeline/PipelineFactory.cs +++ b/src/Hyperbee.Pipeline/PipelineFactory.cs @@ -1,4 +1,6 @@ -namespace Hyperbee.Pipeline; +using System.Linq.Expressions; + +namespace Hyperbee.Pipeline; // A quick note about generic arguments. Remember that the builder methods are // forward-looking and are always building the 'next' step. @@ -30,8 +32,9 @@ public static IPipelineStartBuilder Start() }; } - internal static IPipelineStartBuilder Start( MiddlewareAsync functionMiddleware ) + internal static IPipelineStartBuilder Start( Expression> functionMiddleware ) { + // IPipelineContext context, TInput argument, FunctionAsync next return new PipelineBuilder { Function = ( context, argument ) => Task.FromResult( argument ), From 8ef871d066a30b5077ae5ed40a4f1c37e51d9927 Mon Sep 17 00:00:00 2001 From: Matt Edwards Date: Fri, 9 Aug 2024 13:47:13 -0400 Subject: [PATCH 3/6] WIP: Initial Test passing --- .../Binders/Abstractions/Binder.cs | 4 +- .../Binders/Abstractions/BlockBinder.cs | 2 +- .../Abstractions/ConditionalBlockBinder.cs | 4 +- .../Binders/Abstractions/StatementBinder.cs | 4 +- .../Binders/CallBlockBinder.cs | 48 ++++++----- .../Binders/CallIfBlockBinder.cs | 49 ++++++++--- .../Binders/CallStatementBinder.cs | 70 +++++++-------- .../Binders/ForEachBlockBinder.cs | 62 +++++++------- src/Hyperbee.Pipeline/Binders/HookBinder.cs | 47 ++-------- .../Binders/PipeBlockBinder.cs | 62 +++++++------- .../Binders/PipeIfBlockBinder.cs | 44 ++++++++-- .../Binders/PipeStatementBinder.cs | 77 +++++++---------- .../Binders/ReduceBlockBinder.cs | 63 +++++++------- .../Binders/WaitAllBlockBinder.cs | 83 +++++++++--------- src/Hyperbee.Pipeline/Binders/WrapBinder.cs | 58 +++++++------ .../Builders/CallBlockBuilder.cs | 15 ++-- .../Builders/CallIfBlockBuilder.cs | 7 +- .../Builders/CallStatementBuilder.cs | 6 +- .../Builders/ExpressionBinder.cs | 85 +++---------------- .../Builders/ForEachBlockBuilder.cs | 9 +- .../Builders/PipeIfBlockBuilder.cs | 2 +- .../Builders/PipeStatementBuilder.cs | 10 ++- .../Builders/ReduceBlockBuilder.cs | 2 +- .../Builders/WaitAllBlockBuilder.cs | 10 +-- src/Hyperbee.Pipeline/Builders/WrapBuilder.cs | 4 +- src/Hyperbee.Pipeline/IPipelineFunction.cs | 2 +- src/Hyperbee.Pipeline/PipelineBuilder.cs | 6 +- src/Hyperbee.Pipeline/PipelineFactory.cs | 2 +- .../PipelineDistributedCacheExtensions.cs | 7 +- .../PipelineMemoryCacheExtensions.cs | 11 ++- 30 files changed, 414 insertions(+), 441 deletions(-) diff --git a/src/Hyperbee.Pipeline/Binders/Abstractions/Binder.cs b/src/Hyperbee.Pipeline/Binders/Abstractions/Binder.cs index c77a6ea..18c7f6e 100644 --- a/src/Hyperbee.Pipeline/Binders/Abstractions/Binder.cs +++ b/src/Hyperbee.Pipeline/Binders/Abstractions/Binder.cs @@ -6,9 +6,9 @@ namespace Hyperbee.Pipeline.Binders.Abstractions; internal abstract class Binder { protected Expression> Pipeline { get; } - protected Expression> Configure { get; } + protected Action Configure { get; } - protected Binder( Expression> function, Expression> configure ) + protected Binder( Expression> function, Action configure ) { Pipeline = function; Configure = configure; diff --git a/src/Hyperbee.Pipeline/Binders/Abstractions/BlockBinder.cs b/src/Hyperbee.Pipeline/Binders/Abstractions/BlockBinder.cs index c08361a..31c8ec2 100644 --- a/src/Hyperbee.Pipeline/Binders/Abstractions/BlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/Abstractions/BlockBinder.cs @@ -5,7 +5,7 @@ namespace Hyperbee.Pipeline.Binders.Abstractions; internal abstract class BlockBinder : Binder { - protected BlockBinder( Expression> function, Expression> configure ) + protected BlockBinder( Expression> function, Action configure ) : base( function, configure ) { } diff --git a/src/Hyperbee.Pipeline/Binders/Abstractions/ConditionalBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/Abstractions/ConditionalBlockBinder.cs index c5ccfd0..a579586 100644 --- a/src/Hyperbee.Pipeline/Binders/Abstractions/ConditionalBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/Abstractions/ConditionalBlockBinder.cs @@ -6,9 +6,9 @@ namespace Hyperbee.Pipeline.Binders.Abstractions; internal abstract class ConditionalBlockBinder : BlockBinder { - protected Expression> Condition { get; } + protected Function Condition { get; } - protected ConditionalBlockBinder( Expression> condition, Expression> function, Expression> configure ) + protected ConditionalBlockBinder( Function condition, Expression> function, Action configure ) : base( function, configure ) { Condition = condition; diff --git a/src/Hyperbee.Pipeline/Binders/Abstractions/StatementBinder.cs b/src/Hyperbee.Pipeline/Binders/Abstractions/StatementBinder.cs index e5cc2b2..f468230 100644 --- a/src/Hyperbee.Pipeline/Binders/Abstractions/StatementBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/Abstractions/StatementBinder.cs @@ -6,9 +6,9 @@ namespace Hyperbee.Pipeline.Binders.Abstractions; internal abstract class StatementBinder : Binder { - protected Expression> Middleware { get; } + protected MiddlewareAsync Middleware { get; } - protected StatementBinder( Expression> function, Expression> middleware, Expression> configure ) + protected StatementBinder( Expression> function, MiddlewareAsync middleware, Action configure ) : base( function, configure ) { Middleware = middleware; diff --git a/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs index 1887b27..9e857fa 100644 --- a/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs @@ -15,35 +15,41 @@ public CallBlockBinder( Expression> function ) public Expression> Bind( Expression> next ) { - // Get the MethodInfo for the BindImpl method - var bindImplMethodInfo = typeof( CallBlockBinder ) - .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic )! - .MakeGenericMethod( typeof( TInput ), typeof( TOutput ) ); - - // Create the call expression to BindImpl - var callBind = Expression.Call( - bindImplMethodInfo, + // Get the MethodInfo for the helper method + var bindImplAsyncMethodInfo = typeof(CallBlockBinder) + .GetMethod( nameof(BindImplAsync), BindingFlags.NonPublic | BindingFlags.Instance )!; + + // Create parameters for the lambda expression + var paramContext = Expression.Parameter( typeof(IPipelineContext), "context" ); + var paramArgument = Expression.Parameter( typeof(TInput), "argument" ); + + // Create a call expression to the helper method + var callBindImplAsync = Expression.Call( + Expression.Constant( this ), + bindImplAsyncMethodInfo, next, - Pipeline + Pipeline, + paramContext, + paramArgument ); // Create and return the final expression - var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); - var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); - return Expression.Lambda>( callBind, paramContext, paramArgument ); + return Expression.Lambda>( callBindImplAsync, paramContext, paramArgument ); } - private FunctionAsync BindImpl( FunctionAsync next, FunctionAsync pipeline ) + private async Task BindImplAsync( + FunctionAsync next, + FunctionAsync pipeline, + IPipelineContext context, + TInput argument ) { - return async ( context, argument ) => - { - var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); + var (nextArgument, canceled) = + await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); - if ( canceled ) - return default; + if ( canceled ) + return default; - await ProcessBlockAsync( next, context, nextArgument ).ConfigureAwait( false ); - return nextArgument; - }; + await ProcessBlockAsync( next, context, nextArgument ).ConfigureAwait( false ); + return nextArgument; } } diff --git a/src/Hyperbee.Pipeline/Binders/CallIfBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/CallIfBlockBinder.cs index 18379ae..e72b802 100644 --- a/src/Hyperbee.Pipeline/Binders/CallIfBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/CallIfBlockBinder.cs @@ -1,26 +1,55 @@ using System.Linq.Expressions; +using System.Reflection; using Hyperbee.Pipeline.Binders.Abstractions; +using Hyperbee.Pipeline.Context; namespace Hyperbee.Pipeline.Binders; internal class CallIfBlockBinder : ConditionalBlockBinder { - public CallIfBlockBinder( Expression> condition, Expression> function ) + public CallIfBlockBinder( Function condition, Expression> function ) : base( condition, function, default ) { } - public FunctionAsync Bind( Expression> next ) + public Expression> Bind( Expression> next ) { - return async ( context, argument ) => - { - var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false ); + // Get the MethodInfo for the helper method + var bindImplAsyncMethodInfo = typeof( CallIfBlockBinder ) + .GetMethod( nameof( BindImplAsync ), BindingFlags.NonPublic | BindingFlags.Instance )!; - if ( canceled ) - return default; + // Create parameters for the lambda expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); - await ProcessBlockAsync( next, context, nextArgument ).ConfigureAwait( false ); - return nextArgument; - }; + // Create a call expression to the helper method + var callBindImplAsync = Expression.Call( + Expression.Constant( this ), + bindImplAsyncMethodInfo, + next, + Pipeline, + paramContext, + paramArgument + ); + + // Create and return the final expression + return Expression.Lambda>( callBindImplAsync, paramContext, paramArgument ); } + + private async Task BindImplAsync( + FunctionAsync next, + FunctionAsync pipeline, + IPipelineContext context, + TInput argument ) + { + var (nextArgument, canceled) = + await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); + + if ( canceled ) + return default; + + await ProcessBlockAsync( next, context, nextArgument ).ConfigureAwait( false ); + return nextArgument; + } + } diff --git a/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs b/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs index 958a5b6..a117db9 100644 --- a/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs @@ -7,56 +7,58 @@ namespace Hyperbee.Pipeline.Binders; internal class CallStatementBinder : StatementBinder { - public CallStatementBinder( Expression> function, Expression> middleware, Expression> configure ) + public CallStatementBinder( Expression> function, MiddlewareAsync middleware, Action configure ) : base( function, middleware, configure ) { } public Expression> Bind( ProcedureAsync next, MethodInfo method = null ) { - // Get the MethodInfo for the BindImpl method - var bindImplMethodInfo = typeof( CallStatementBinder ) - .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic )! - .MakeGenericMethod( typeof( TInput ), typeof( TOutput ) ); - - // Create the call expression to BindImpl - var callBind = Expression.Call( - bindImplMethodInfo, + var defaultName = (method ?? next.Method).Name; + + // Get the MethodInfo for the helper method + var bindImplAsyncMethodInfo = typeof( CallStatementBinder) + .GetMethod( nameof(BindImplAsync), BindingFlags.NonPublic | BindingFlags.Instance )!; + + // Create parameters for the lambda expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); + + // Create a call expression to the helper method + var callBindImplAsync = Expression.Call( + Expression.Constant( this ), + bindImplAsyncMethodInfo, ExpressionBinder.ToExpression( next ), Pipeline, - Configure, - Expression.Constant( method, typeof( MethodInfo ) ) + paramContext, + paramArgument, + Expression.Constant( defaultName ) ); // Create and return the final expression - var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); - var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); - return Expression.Lambda>(callBind, paramContext, paramArgument); + return Expression.Lambda>( callBindImplAsync, paramContext, paramArgument ); } - public FunctionAsync BindImpl( - ProcedureAsync next, + private async Task BindImplAsync( + ProcedureAsync next, FunctionAsync pipeline, - MiddlewareAsync middleware, - Action configure, - MethodInfo method = null ) + IPipelineContext context, + TInput argument, + string defaultName ) { - var defaultName = (method ?? next.Method).Name; + var (nextArgument, canceled) = + await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); + + if ( canceled ) + return default; + + return await ProcessStatementAsync( + async ( ctx, arg ) => + { + await next( ctx, arg ).ConfigureAwait( false ); + return arg; + }, context, nextArgument, defaultName ).ConfigureAwait( false ); - return async ( context, argument ) => - { - var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); - - if ( canceled ) - return default; - - return await ProcessStatementAsync( - async ( ctx, arg ) => - { - await next( ctx, arg ).ConfigureAwait( false ); - return arg; - }, context, nextArgument, defaultName ).ConfigureAwait( false ); - }; } /* diff --git a/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs index 008cd3d..c6b59c6 100644 --- a/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs @@ -8,50 +8,54 @@ namespace Hyperbee.Pipeline.Binders; internal class ForEachBlockBinder : BlockBinder { - public ForEachBlockBinder( FunctionAsync function ) + public ForEachBlockBinder( Expression> function ) : base( function, default ) { } - - public Expression> Bind( FunctionAsync next ) + public Expression> Bind( Expression> next ) { - // Get the MethodInfo for the BindImpl method - var bindImplMethodInfo = typeof( ForEachBlockBinder ) - .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic )! - .MakeGenericMethod( typeof( TInput ), typeof( TOutput ) ); - - // Create the call expression to BindImpl - var callBind = Expression.Call( - bindImplMethodInfo, - ExpressionBinder.ToExpression( next ), - Pipeline - ); + // Get the MethodInfo for the helper method + var bindImplAsyncMethodInfo = typeof( ForEachBlockBinder ) + .GetMethod( nameof( BindImplAsync ), BindingFlags.NonPublic | BindingFlags.Instance )!; - // Create and return the final expression + // Create parameters for the lambda expression var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); - return Expression.Lambda>( callBind, paramContext, paramArgument ); + + // Create a call expression to the helper method + var callBindImplAsync = Expression.Call( + Expression.Constant( this ), + bindImplAsyncMethodInfo, + next, + Pipeline, + paramContext, + paramArgument + ); + + // Create and return the final expression + return Expression.Lambda>( callBindImplAsync, paramContext, paramArgument ); } - private FunctionAsync BindImpl( FunctionAsync next, FunctionAsync pipeline ) + private async Task BindImplAsync( + FunctionAsync next, + FunctionAsync pipeline, + IPipelineContext context, + TInput argument ) { - return async ( context, argument ) => - { - var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); - if ( canceled ) - return default; + if ( canceled ) + return default; - var nextArguments = (IEnumerable) nextArgument; + var nextArguments = (IEnumerable) nextArgument; - foreach ( var elementArgument in nextArguments ) - { - await ProcessBlockAsync( next, context, elementArgument ).ConfigureAwait( false ); - } + foreach ( var elementArgument in nextArguments ) + { + await ProcessBlockAsync( next, context, elementArgument ).ConfigureAwait( false ); + } - return nextArgument; - }; + return nextArgument; } } diff --git a/src/Hyperbee.Pipeline/Binders/HookBinder.cs b/src/Hyperbee.Pipeline/Binders/HookBinder.cs index 569c043..12fc935 100644 --- a/src/Hyperbee.Pipeline/Binders/HookBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/HookBinder.cs @@ -1,57 +1,24 @@ -using System.Linq.Expressions; -using Hyperbee.Pipeline.Context; -using System.Reflection; +using static System.Net.Mime.MediaTypeNames; namespace Hyperbee.Pipeline.Binders; internal class HookBinder // explicit Type Args due to usage { - private Expression> Middleware { get; } + private MiddlewareAsync Middleware { get; } - public HookBinder( Expression> middleware ) + public HookBinder( MiddlewareAsync middleware ) { - if ( middleware == null ) - { - // Create and return the final expression - var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); - var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); - Middleware = Expression.Lambda>( - null - // TODO: empty middleware async ( context, argument, next ) => await next( context, argument ).ConfigureAwait( false ) - , paramContext, paramArgument ); - } - - this.Middleware = middleware; - - } - - public Expression> Bind( MiddlewareAsync middleware ) - { - // Get the MethodInfo for the BindImpl method - var bindImplMethodInfo = typeof( HookBinder ) - .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic )! - .MakeGenericMethod( typeof( TInput ), typeof( TOutput ) ); - - // Create the call expression to BindImpl - var callBind = Expression.Call( - bindImplMethodInfo, - ExpressionBinder.ToExpression( middleware ), - Middleware - ); - - // Create and return the final expression - var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); - var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); - return Expression.Lambda>( callBind, paramContext, paramArgument ); + Middleware = middleware ?? (async ( context, argument, next ) => + await next( context, argument ).ConfigureAwait( false )); } - private MiddlewareAsync BindImpl( MiddlewareAsync middleware, MiddlewareAsync inner ) + public MiddlewareAsync Bind( MiddlewareAsync middleware ) { return async ( context, argument, function ) => await middleware( context, argument, - async ( context1, argument1 ) => await inner( context1, argument1, function ).ConfigureAwait( false ) + async ( context1, argument1 ) => await Middleware( context1, argument1, function ).ConfigureAwait( false ) ).ConfigureAwait( false ); } } diff --git a/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs index bad245d..6607863 100644 --- a/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs @@ -2,6 +2,7 @@ using System.Linq.Expressions; using System.Reflection; +using Hyperbee.Pipeline.Context; namespace Hyperbee.Pipeline.Binders; @@ -14,45 +15,40 @@ public PipeBlockBinder( Expression> function ) public Expression> Bind( Expression> next ) { - var paramContext = Expression.Parameter( typeof( TInput ), "context" ); - var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); - - var invokePipeline = Expression.Invoke( Pipeline, paramContext, paramArgument ); - - var invokeNext = Expression.Invoke( - next, - paramContext, - invokePipeline ); - - return Expression.Lambda>( invokeNext, paramContext, paramArgument ); - } - -} + // Get the MethodInfo for the helper method + var bindImplAsyncMethodInfo = typeof( PipeBlockBinder ) + .GetMethod( nameof( BindImplAsync ), BindingFlags.NonPublic | BindingFlags.Instance )! + .MakeGenericMethod( typeof( TNext ) ); + // Create parameters for the lambda expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); -/* - -internal class PipeBlockBinder -{ - private FunctionAsync Pipeline { get; } - private Function Condition { get; } - - public PipeBlockBinder( FunctionAsync function ) - : base( function, default ) - { + // Create a call expression to the helper method + var callBindImplAsync = Expression.Call( + Expression.Constant( this ), + bindImplAsyncMethodInfo, + next, + Pipeline, + paramContext, + paramArgument + ); + + // Create and return the final expression + return Expression.Lambda>( callBindImplAsync, paramContext, paramArgument ); } - public FunctionAsync Bind( FunctionAsync next ) + private async Task BindImplAsync( + FunctionAsync next, + FunctionAsync pipeline, + IPipelineContext context, + TInput argument ) { - return async ( context, argument ) => - { - var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false ); + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); - if ( canceled ) - return default; + if ( canceled ) + return default; - return await ProcessBlockAsync( next, context, nextArgument ).ConfigureAwait( false ); - }; + return await ProcessBlockAsync( next, context, nextArgument ).ConfigureAwait( false ); } } -*/ diff --git a/src/Hyperbee.Pipeline/Binders/PipeIfBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/PipeIfBlockBinder.cs index cdc0a34..15ddf5f 100644 --- a/src/Hyperbee.Pipeline/Binders/PipeIfBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/PipeIfBlockBinder.cs @@ -1,25 +1,53 @@ using System.Linq.Expressions; +using System.Reflection; using Hyperbee.Pipeline.Binders.Abstractions; +using Hyperbee.Pipeline.Context; namespace Hyperbee.Pipeline.Binders; internal class PipeIfBlockBinder : ConditionalBlockBinder { - public PipeIfBlockBinder( Expression> condition, Expression> function ) + public PipeIfBlockBinder( Function condition, Expression> function ) : base( condition, function, default ) { } public Expression> Bind( Expression> next ) { - return async ( context, argument ) => - { - var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false ); + // Get the MethodInfo for the helper method + var bindImplAsyncMethodInfo = typeof( PipeIfBlockBinder ) + .GetMethod( nameof( BindImplAsync ), BindingFlags.NonPublic | BindingFlags.Instance )! + .MakeGenericMethod( typeof( TNext ) ); - if ( canceled ) - return default; + // Create parameters for the lambda expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); - return await ProcessBlockAsync( next, context, nextArgument ).ConfigureAwait( false ); - }; + // Create a call expression to the helper method + var callBindImplAsync = Expression.Call( + Expression.Constant( this ), + bindImplAsyncMethodInfo, + next, + Pipeline, + paramContext, + paramArgument + ); + + // Create and return the final expression + return Expression.Lambda>( callBindImplAsync, paramContext, paramArgument ); + } + + private async Task BindImplAsync( + FunctionAsync next, + FunctionAsync pipeline, + IPipelineContext context, + TInput argument ) + { + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); + + if ( canceled ) + return default; + + return await ProcessBlockAsync( next, context, nextArgument ).ConfigureAwait( false ); } } diff --git a/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs b/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs index 678f86c..d785957 100644 --- a/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs @@ -7,69 +7,54 @@ namespace Hyperbee.Pipeline.Binders; internal class PipeStatementBinder : StatementBinder { - public PipeStatementBinder( Expression> function, Expression> middleware, Expression> configure ) + public PipeStatementBinder( Expression> function, + MiddlewareAsync middleware, Action configure ) : base( function, middleware, configure ) { } - public Expression> Bind( FunctionAsync next, MethodInfo method = null ) + public Expression> Bind( FunctionAsync next, + MethodInfo method = null ) { - // Get the MethodInfo for the BindImpl method - var bindImplMethodInfo = typeof( PipeStatementBinder ) - .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic )! - .MakeGenericMethod( typeof( TInput ), typeof( TOutput ), typeof( TNext ) ); + var defaultName = (method ?? next.Method).Name; + + // Get the MethodInfo for the helper method + var bindImplAsyncMethodInfo = typeof(PipeStatementBinder) + .GetMethod( nameof(BindImplAsync), BindingFlags.NonPublic | BindingFlags.Instance )! + .MakeGenericMethod( typeof(TNext) ); - // Create the call expression to BindImpl - var callBind = Expression.Call( - bindImplMethodInfo, + // Create parameters for the lambda expression + var paramContext = Expression.Parameter( typeof(IPipelineContext), "context" ); + var paramArgument = Expression.Parameter( typeof(TInput), "argument" ); + + // Create a call expression to the helper method + var callBindImplAsync = Expression.Call( + Expression.Constant( this ), + bindImplAsyncMethodInfo, ExpressionBinder.ToExpression( next ), Pipeline, - Middleware, - Configure, - Expression.Constant( method, typeof( MethodInfo ) ) + paramContext, + paramArgument, + Expression.Constant( defaultName ) ); // Create and return the final expression - var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); - var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); - return Expression.Lambda>( callBind, paramContext, paramArgument ); - + return Expression.Lambda>( callBindImplAsync, paramContext, paramArgument ); } - private FunctionAsync BindImpl( + private async Task BindImplAsync( FunctionAsync next, FunctionAsync pipeline, - MiddlewareAsync middleware, - Action configure, - MethodInfo method = null ) + IPipelineContext context, + TInput argument, + string defaultName ) { - var defaultName = (method ?? next.Method).Name; - - return async ( context, argument ) => - { - var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); + var (nextArgument, canceled) = + await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); - if ( canceled ) - return default; + if ( canceled ) + return default; - return await ProcessStatementAsync( next, context, nextArgument, defaultName ).ConfigureAwait( false ); - }; + return await ProcessStatementAsync( next, context, nextArgument, defaultName ).ConfigureAwait( false ); } - - /* - private static async Task Next( - FunctionAsync next, - MiddlewareAsync middleware, - IPipelineContext context, - TOutput nextArgument ) - { - if ( middleware == null ) - return await next( context, nextArgument ).ConfigureAwait( false ); - - return (TNext) await middleware( - context, - nextArgument, - async ( context1, argument1 ) => await next( context1, (TOutput) argument1 ).ConfigureAwait( false ) - ).ConfigureAwait( false ); - }*/ } diff --git a/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs index 5c2f479..dadf350 100644 --- a/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs @@ -16,47 +16,52 @@ public ReduceBlockBinder( Func reducer, Expression> Bind( FunctionAsync next ) + public Expression> Bind( Expression> next ) { // Get the MethodInfo for the BindImpl method - var bindImplMethodInfo = typeof( ReduceBlockBinder ) - .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic )! - .MakeGenericMethod( typeof( TInput ), typeof( TOutput ), typeof( TNext ) ); - - // Create the call expression to BindImpl - var callBind = Expression.Call( - bindImplMethodInfo, - ExpressionBinder.ToExpression( next ), - Pipeline - ); + var bindImplAsyncMethodInfo = typeof( ReduceBlockBinder ) + .GetMethod( nameof( BindImplAsync ), BindingFlags.NonPublic | BindingFlags.Instance )!; - // Create and return the final expression + // Create parameters for the lambda expression var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); - return Expression.Lambda>( callBind, paramContext, paramArgument ); + + // Create a call expression to the helper method + var callBindImplAsync = Expression.Call( + Expression.Constant( this ), + bindImplAsyncMethodInfo, + next, + Pipeline, + paramContext, + paramArgument + ); + + // Create and return the final expression + return Expression.Lambda>( callBindImplAsync, paramContext, paramArgument ); } - private FunctionAsync BindImpl( FunctionAsync next, FunctionAsync pipeline ) + private async Task BindImplAsync( + FunctionAsync next, + FunctionAsync pipeline, + IPipelineContext context, + TInput argument ) { - return async ( context, argument ) => - { - var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); - if ( canceled ) - return default; + if ( canceled ) + return default; - var nextArguments = (IEnumerable) nextArgument; - var accumulator = default( TNext ); + var nextArguments = (IEnumerable) nextArgument; + var accumulator = default( TNext ); - // Process each element and apply the reducer - foreach ( var elementArgument in nextArguments ) - { - var result = await ProcessBlockAsync( next, context, elementArgument ).ConfigureAwait( false ); - accumulator = Reducer( accumulator, result ); - } + // Process each element and apply the reducer + foreach ( var elementArgument in nextArguments ) + { + var result = await ProcessBlockAsync( next, context, elementArgument ).ConfigureAwait( false ); + accumulator = Reducer( accumulator, result ); + } - return accumulator; - }; + return accumulator; } } diff --git a/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs index ba05240..3857b04 100644 --- a/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs @@ -7,78 +7,73 @@ namespace Hyperbee.Pipeline.Binders; - internal class WaitAllBlockBinder : ConditionalBlockBinder { - private Expression> Middleware { get; } + private MiddlewareAsync Middleware { get; } public WaitAllBlockBinder( Expression> function, - Expression> middleware, - Expression> configure ) + MiddlewareAsync middleware, + Action configure ) : base( null, function, configure ) { Middleware = middleware; } public WaitAllBlockBinder( - Expression> condition, + Function condition, Expression> function, - Expression> middleware, - Expression> configure ) + MiddlewareAsync middleware, + Action configure ) : base( condition, function, configure ) { Middleware = middleware; } - public Expression> Bind( FunctionAsync[] next, WaitAllReducer reducer ) + public Expression> Bind( FunctionAsync[] nexts, WaitAllReducer reducer ) { - // Get the MethodInfo for the BindImpl method - var bindImplMethodInfo = typeof( WaitAllBlockBinder ) - .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic | BindingFlags.Static )! - .MakeGenericMethod( typeof( TInput ), typeof( TOutput ), typeof( TNext ) ); - - // Create the call expression to BindImpl - var callBind = Expression.Call( - bindImplMethodInfo, - next, + // Get the MethodInfo for the helper method + var bindImplAsyncMethodInfo = typeof( WaitAllBlockBinder ) + .GetMethod( nameof( BindImplAsync ), BindingFlags.NonPublic | BindingFlags.Instance )! + .MakeGenericMethod( typeof( TNext ) ); + + // Create parameters for the lambda expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); + + // Create a call expression to the helper method + var callBindImplAsync = Expression.Call( + Expression.Constant( this ), + bindImplAsyncMethodInfo, + Expression.Constant( nexts ), + Expression.Constant( reducer ), Pipeline, - Middleware, - Configure, - Condition, - Expression.Constant( reducer ) + paramContext, + paramArgument ); // Create and return the final expression - var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); - var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); - return Expression.Lambda>( callBind, paramContext, paramArgument ); + return Expression.Lambda>( callBindImplAsync, paramContext, paramArgument ); } - public FunctionAsync BindImpl( - FunctionAsync[] nexts, - FunctionAsync pipeline, - MiddlewareAsync middleware, - Action configure, - Function condition, - WaitAllReducer reducer ) + private async Task BindImplAsync( + FunctionAsync[] nexts, + WaitAllReducer reducer, + FunctionAsync pipeline, + IPipelineContext context, + TInput argument ) { - ArgumentNullException.ThrowIfNull( reducer ); - - return async ( context, argument ) => - { - var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument, pipeline ).ConfigureAwait( false ); - if ( canceled ) - return default; + if ( canceled ) + return default; - // WaitAllBlockBinder is unique in that it is both a block configure and a step. - // The reducer is the step action, and because it is a step, we need to ensure - // that middleware is called. Middleware requires us to pass in the execution - // function that it wraps. This requires an additional level of wrapping. + // WaitAllBlockBinder is unique in that it is both a block configure and a step. + // The reducer is the step action, and because it is a step, we need to ensure + // that middleware is called. Middleware requires us to pass in the execution + // function that it wraps. This requires an additional level of wrapping. - return await WaitAllAsync( context, nextArgument, nexts, reducer ).ConfigureAwait( false ); - }; + return await WaitAllAsync( context, nextArgument, nexts, reducer ).ConfigureAwait( false ); } private async Task WaitAllAsync( IPipelineContext context, TOutput nextArgument, FunctionAsync[] nexts, WaitAllReducer reducer ) diff --git a/src/Hyperbee.Pipeline/Binders/WrapBinder.cs b/src/Hyperbee.Pipeline/Binders/WrapBinder.cs index bdd59a9..39daf58 100644 --- a/src/Hyperbee.Pipeline/Binders/WrapBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/WrapBinder.cs @@ -8,50 +8,54 @@ namespace Hyperbee.Pipeline.Binders; internal class WrapBinder { private Expression> Middleware { get; } - private Expression> Configure { get; } + private Action Configure { get; } - public WrapBinder( Expression> middleware, Expression> configure ) + public WrapBinder( Expression> middleware, Action configure ) { Middleware = middleware; Configure = configure; } - public Expression> Bind( FunctionAsync next ) + public Expression> Bind( Expression> next ) { - // Get the MethodInfo for the BindImpl method - var bindImplMethodInfo = typeof( WrapBinder ) - .GetMethod( nameof( BindImpl ), BindingFlags.NonPublic | BindingFlags.Static )! - .MakeGenericMethod( typeof( TInput ), typeof( TOutput ) ); - - // Create the call expression to BindImpl - var callBind = Expression.Call( - bindImplMethodInfo, - ExpressionBinder.ToExpression( next ), + // Get the MethodInfo for the helper method + var bindImplAsyncMethodInfo = typeof( WrapBinder ) + .GetMethod( nameof( BindImplAsync ), BindingFlags.NonPublic | BindingFlags.Instance )!; + + // Create parameters for the lambda expression + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); + + // Create a call expression to the helper method + var callBindImplAsync = Expression.Call( + Expression.Constant( this ), + bindImplAsyncMethodInfo, + next, Middleware, - Configure + paramContext, + paramArgument ); // Create and return the final expression - var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); - var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); - return Expression.Lambda>( callBind, paramContext, paramArgument ); + return Expression.Lambda>( callBindImplAsync, paramContext, paramArgument ); } - private static FunctionAsync BindImpl( FunctionAsync next, MiddlewareAsync middleware, Action configure ) + private async Task BindImplAsync( + FunctionAsync next, + MiddlewareAsync middleware, + IPipelineContext context, + TInput argument ) { var defaultName = next.Method.Name; - return async ( context, argument ) => - { - var contextControl = (IPipelineContextControl) context; + var contextControl = (IPipelineContextControl) context; - using var _ = contextControl.CreateFrame( context, configure, defaultName ); + using var _ = contextControl.CreateFrame( context, Configure, defaultName ); - return await middleware( - context, - argument, - async ( context1, argument1 ) => await next( context1, argument1 ).ConfigureAwait( false ) - ).ConfigureAwait( false ); - }; + return await middleware( + context, + argument, + async ( context1, argument1 ) => await next( context1, argument1 ).ConfigureAwait( false ) + ).ConfigureAwait( false ); } } diff --git a/src/Hyperbee.Pipeline/Builders/CallBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/CallBlockBuilder.cs index 6ada283..4bf898c 100644 --- a/src/Hyperbee.Pipeline/Builders/CallBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/CallBlockBuilder.cs @@ -1,6 +1,4 @@ -using System; -using Hyperbee.Pipeline.Binders; -using static System.Runtime.InteropServices.JavaScript.JSType; +using Hyperbee.Pipeline.Binders; namespace Hyperbee.Pipeline; @@ -14,21 +12,24 @@ public partial class PipelineBuilder { // Call an inner builder discarding the final result. Acts like an Action. - public IPipelineBuilder Call( Func, IPipelineBuilder> builder ) + public IPipelineBuilder Call( + Func, IPipelineBuilder> builder ) { return Call( true, builder ); } - public IPipelineBuilder Call( bool inheritMiddleware, Func, IPipelineBuilder> builder ) + public IPipelineBuilder Call( bool inheritMiddleware, + Func, IPipelineBuilder> builder ) { ArgumentNullException.ThrowIfNull( builder ); var block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ); - var function = builder( block ).CastFunction(); // cast because we don't know the final Pipe output value + var function = builder( block ).CastFunction(); // cast because we don't know the final Pipe output value return new PipelineBuilder { - Function = new CallBlockBinder( Function ).Bind( function ), + Function = + new CallBlockBinder( Function ).Bind( ExpressionBinder.ToExpression( function ) ), Middleware = Middleware }; } diff --git a/src/Hyperbee.Pipeline/Builders/CallIfBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/CallIfBlockBuilder.cs index 9c8b6fb..ce571af 100644 --- a/src/Hyperbee.Pipeline/Builders/CallIfBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/CallIfBlockBuilder.cs @@ -21,13 +21,12 @@ public IPipelineBuilder CallIf( Function conditi ArgumentNullException.ThrowIfNull( condition ); var block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ); - var function = builder( block ).CastFunction(); // cast because we don't know the final Pipe output value + //var function = builder( block ).CastFunction(); // cast because we don't know the final Pipe output value + var function = ((PipelineBuilder) builder( block )).Function; return new PipelineBuilder { - Function = new CallIfBlockBinder( - ExpressionBinder.ToExpression( condition ), - Function ).Bind( function ), + Function = new CallIfBlockBinder( condition, Function ).Bind( function ), Middleware = Middleware }; } diff --git a/src/Hyperbee.Pipeline/Builders/CallStatementBuilder.cs b/src/Hyperbee.Pipeline/Builders/CallStatementBuilder.cs index f9c8e0c..1f370cf 100644 --- a/src/Hyperbee.Pipeline/Builders/CallStatementBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/CallStatementBuilder.cs @@ -21,7 +21,11 @@ public IPipelineBuilder Call( Procedure next, Action { - Function = new CallStatementBinder( Function, Middleware, config ).Bind( AsyncNext, next.Method ), + Function = new CallStatementBinder( + Function, + Middleware, + config + ).Bind( AsyncNext, next.Method ), Middleware = Middleware }; diff --git a/src/Hyperbee.Pipeline/Builders/ExpressionBinder.cs b/src/Hyperbee.Pipeline/Builders/ExpressionBinder.cs index cbe3438..569cd89 100644 --- a/src/Hyperbee.Pipeline/Builders/ExpressionBinder.cs +++ b/src/Hyperbee.Pipeline/Builders/ExpressionBinder.cs @@ -10,83 +10,24 @@ public static Expression> ToExpression( Action>( - Expression.Invoke( Expression.Constant( action ), contextParameter ), - contextParameter - ); - } - - public static Expression> ToExpression( Func func ) - { - var inputParameter = Expression.Parameter( typeof( TInput ), "input" ); - - return Expression.Lambda>( - Expression.Invoke( Expression.Constant( func ), inputParameter ), - inputParameter - ); + return (context) => action(context); } - public static Expression> ToExpression( Function function ) - { - var contextParameter = Expression.Parameter( typeof( IPipelineContext ), "context" ); - var inputParameter = Expression.Parameter( typeof( TInput ), "input" ); + public static Expression> ToExpression( Func func ) => + input => func( input ); - return Expression.Lambda>( - Expression.Invoke( Expression.Constant( function ), contextParameter, inputParameter ), - contextParameter, - inputParameter - ); - } + public static Expression> ToExpression( Function function ) => + (context, argument) => function(context, argument); - public static Expression> ToExpression( Procedure function ) - { - var contextParameter = Expression.Parameter( typeof( IPipelineContext ), "context" ); - var inputParameter = Expression.Parameter( typeof( TInput ), "input" ); + public static Expression> ToExpression( Procedure function ) => + (context, argument) => function(context, argument); - return Expression.Lambda>( - Expression.Invoke( Expression.Constant( function ), contextParameter, inputParameter ), - contextParameter, - inputParameter - ); - } + public static Expression> ToExpression( FunctionAsync function ) => + ( context, input ) => function( context, input ); - public static Expression> ToExpression( FunctionAsync function ) - { - var contextParameter = Expression.Parameter( typeof( IPipelineContext ), "context" ); - var inputParameter = Expression.Parameter( typeof( TInput ), "input" ); + public static Expression> ToExpression( ProcedureAsync function ) => + (context, argument) => function(context, argument); - return Expression.Lambda>( - Expression.Invoke( Expression.Constant( function ), contextParameter, inputParameter ), - contextParameter, - inputParameter - ); - } - - public static Expression> ToExpression( ProcedureAsync function ) - { - var contextParameter = Expression.Parameter( typeof( IPipelineContext ), "context" ); - var inputParameter = Expression.Parameter( typeof( TInput ), "input" ); - - return Expression.Lambda>( - Expression.Invoke( Expression.Constant( function ), contextParameter, inputParameter ), - contextParameter, - inputParameter - ); - } - - public static Expression> ToExpression( MiddlewareAsync middleware ) - { - var contextParameter = Expression.Parameter( typeof( IPipelineContext ), "context" ); - var inputParameter = Expression.Parameter( typeof( TInput ), "input" ); - var nextParameter = Expression.Parameter( typeof( FunctionAsync ), "next" ); - - return Expression.Lambda>( - Expression.Invoke( Expression.Constant( middleware ), contextParameter, inputParameter, nextParameter ), - contextParameter, - inputParameter, - nextParameter - ); - } + public static Expression> ToExpression( MiddlewareAsync middleware ) => + (context, argument, next) => middleware(context, argument, next); } diff --git a/src/Hyperbee.Pipeline/Builders/ForEachBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/ForEachBlockBuilder.cs index 384c1bf..1789449 100644 --- a/src/Hyperbee.Pipeline/Builders/ForEachBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/ForEachBlockBuilder.cs @@ -10,12 +10,14 @@ public partial interface IPipelineBuilder public partial class PipelineBuilder { - public IPipelineBuilder ForEach( Func, IPipelineBuilder> builder ) + public IPipelineBuilder ForEach( + Func, IPipelineBuilder> builder ) { return ForEachAsync( true, builder ); } - public IPipelineBuilder ForEachAsync( bool inheritMiddleware, Func, IPipelineBuilder> builder ) + public IPipelineBuilder ForEachAsync( bool inheritMiddleware, + Func, IPipelineBuilder> builder ) { ArgumentNullException.ThrowIfNull( builder ); @@ -24,7 +26,8 @@ public IPipelineBuilder ForEachAsync( bool inheritMid return new PipelineBuilder { - Function = new ForEachBlockBinder( Function ).Bind( function ), + Function = new ForEachBlockBinder( Function ).Bind( + ExpressionBinder.ToExpression( function ) ), Middleware = Middleware }; } diff --git a/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs index ba78e38..91d8922 100644 --- a/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs @@ -26,7 +26,7 @@ public IPipelineBuilder PipeIf( Function co return new PipelineBuilder { Function = new PipeIfBlockBinder( - ExpressionBinder.ToExpression( condition ), + condition, Function ).Bind( function ), Middleware = Middleware }; diff --git a/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs b/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs index 146f1da..c9f8ce5 100644 --- a/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs @@ -21,7 +21,10 @@ public IPipelineBuilder Pipe( Function nex return new PipelineBuilder { - Function = new PipeStatementBinder( Function, Middleware, config ).Bind( AsyncNext, next.Method ), + Function = new PipeStatementBinder( + Function, + Middleware, + config ).Bind( AsyncNext, next.Method ), Middleware = Middleware }; @@ -41,7 +44,10 @@ public IPipelineBuilder PipeAsync( FunctionAsync { - Function = new PipeStatementBinder( Function, Middleware, config ).Bind( next ), + Function = new PipeStatementBinder( + Function, + Middleware, + config ).Bind( next ), Middleware = Middleware }; } diff --git a/src/Hyperbee.Pipeline/Builders/ReduceBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/ReduceBlockBuilder.cs index 2910aad..4552cf7 100644 --- a/src/Hyperbee.Pipeline/Builders/ReduceBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/ReduceBlockBuilder.cs @@ -21,7 +21,7 @@ public IPipelineBuilder ReduceAsync( bool inheri ArgumentNullException.ThrowIfNull( reducer ); var block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ); - var function = ((PipelineBuilder) builder( block )).Function; + var function = ((PipelineBuilder) builder( block )).Function; return new PipelineBuilder { diff --git a/src/Hyperbee.Pipeline/Builders/WaitAllBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/WaitAllBlockBuilder.cs index 3e8cb82..28166ed 100644 --- a/src/Hyperbee.Pipeline/Builders/WaitAllBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/WaitAllBlockBuilder.cs @@ -16,8 +16,7 @@ public partial interface IPipelineBuilder public partial class PipelineBuilder { public IPipelineBuilder WaitAll( - Func, - Func, IPipelineBuilder>[]> builders, + Func, Func, IPipelineBuilder>[]> builders, WaitAllReducer reducer, Action config = null ) { @@ -25,8 +24,7 @@ public IPipelineBuilder WaitAll( } public IPipelineBuilder WaitAll( - Func, - Func, + Func, Func, IPipelineBuilder>[]> builders, Action config = null ) { @@ -47,8 +45,7 @@ public IPipelineBuilder WaitAll( public IPipelineBuilder WaitAll( bool inheritMiddleware, - Func, - Func, IPipelineBuilder>[]> builders, + Func, Func, IPipelineBuilder>[]> builders, WaitAllReducer reducer, Action config = null ) { @@ -61,6 +58,7 @@ public IPipelineBuilder WaitAll( var functions = builderInstances .Select( builder => new { builder, block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ) } ) + //.Select( x => ((PipelineBuilder) x.builder( x.block )).Function ) .Select( x => x.builder( x.block ).CastFunction() ) .ToArray(); diff --git a/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs b/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs index 8e292e4..cf5af17 100644 --- a/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs @@ -21,7 +21,9 @@ public IPipelineBuilder WrapAsync( MiddlewareAsync { - Function = new WrapBinder( pipelineMiddleware, config ).Bind( Function ), + Function = new WrapBinder( + ExpressionBinder.ToExpression( pipelineMiddleware ), + config ).Bind( Function ), Middleware = Middleware }; } diff --git a/src/Hyperbee.Pipeline/IPipelineFunction.cs b/src/Hyperbee.Pipeline/IPipelineFunction.cs index 90845fa..4905fa4 100644 --- a/src/Hyperbee.Pipeline/IPipelineFunction.cs +++ b/src/Hyperbee.Pipeline/IPipelineFunction.cs @@ -12,5 +12,5 @@ public interface IPipelineFunctionProvider public interface IPipelineFunction { Expression> Function { get; } - Expression> Middleware { get; } + MiddlewareAsync Middleware { get; } } diff --git a/src/Hyperbee.Pipeline/PipelineBuilder.cs b/src/Hyperbee.Pipeline/PipelineBuilder.cs index e992ba9..ff820ed 100644 --- a/src/Hyperbee.Pipeline/PipelineBuilder.cs +++ b/src/Hyperbee.Pipeline/PipelineBuilder.cs @@ -6,7 +6,7 @@ namespace Hyperbee.Pipeline; public partial class PipelineBuilder : PipelineFactory, IPipelineStartBuilder, IPipelineFunctionProvider { internal Expression> Function { get; init; } - internal Expression> Middleware { get; init; } + internal MiddlewareAsync Middleware { get; init; } internal PipelineBuilder() { @@ -84,9 +84,9 @@ IPipelineFunction IPipelineFunctionProvider.Ge public record PipelineFunction : IPipelineFunction { public Expression> Function { get; init; } - public Expression> Middleware { get; init; } + public MiddlewareAsync Middleware { get; init; } - public void Deconstruct( out Expression> function, out Expression> middleware ) + public void Deconstruct( out Expression> function, out MiddlewareAsync middleware ) { function = Function; middleware = Middleware; diff --git a/src/Hyperbee.Pipeline/PipelineFactory.cs b/src/Hyperbee.Pipeline/PipelineFactory.cs index abc97c0..ce9d2df 100644 --- a/src/Hyperbee.Pipeline/PipelineFactory.cs +++ b/src/Hyperbee.Pipeline/PipelineFactory.cs @@ -32,7 +32,7 @@ public static IPipelineStartBuilder Start() }; } - internal static IPipelineStartBuilder Start( Expression> functionMiddleware ) + internal static IPipelineStartBuilder Start( MiddlewareAsync functionMiddleware ) { // IPipelineContext context, TInput argument, FunctionAsync next return new PipelineBuilder diff --git a/src/Hyperbee.Pipline.Caching/PipelineDistributedCacheExtensions.cs b/src/Hyperbee.Pipline.Caching/PipelineDistributedCacheExtensions.cs index 6fd61ee..bec22a7 100644 --- a/src/Hyperbee.Pipline.Caching/PipelineDistributedCacheExtensions.cs +++ b/src/Hyperbee.Pipline.Caching/PipelineDistributedCacheExtensions.cs @@ -1,5 +1,4 @@ -using Hyperbee.Pipeline.Extensions.Implementation; -using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -16,9 +15,9 @@ public static IPipelineBuilder PipeDistributedCacheAsync(); - var function = nestedBuilder( block ).GetPipelineFunction(); + var function = nestedBuilder( block ).CastFunction(); - return builder.PipeDistributedCacheAsync( function.Function, optionsFunc ); + return builder.PipeDistributedCacheAsync( function, optionsFunc ); } public static IPipelineBuilder PipeDistributedCacheAsync( diff --git a/src/Hyperbee.Pipline.Caching/PipelineMemoryCacheExtensions.cs b/src/Hyperbee.Pipline.Caching/PipelineMemoryCacheExtensions.cs index ab19f2e..ac3f325 100644 --- a/src/Hyperbee.Pipline.Caching/PipelineMemoryCacheExtensions.cs +++ b/src/Hyperbee.Pipline.Caching/PipelineMemoryCacheExtensions.cs @@ -1,5 +1,4 @@ -using Hyperbee.Pipeline.Extensions.Implementation; -using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -16,9 +15,9 @@ public static IPipelineBuilder PipeCache( ArgumentNullException.ThrowIfNull( nestedBuilder ); var block = PipelineFactory.Start(); - var function = nestedBuilder( block ).GetPipelineFunction(); + var function = nestedBuilder( block ).CastFunction(); - return builder.PipeCacheAsync( function.Function, optionsFunc ); + return builder.PipeCacheAsync( function, optionsFunc ); } public static IPipelineBuilder PipeCacheAsync( @@ -29,9 +28,9 @@ public static IPipelineBuilder PipeCacheAsync(); - var function = nestedBuilder( block ).GetPipelineFunction(); + var function = nestedBuilder( block ).CastFunction(); - return builder.PipeCacheAsync( function.Function, optionsFunc ); + return builder.PipeCacheAsync( function, optionsFunc ); } public static IPipelineBuilder PipeCache( From ccdb3b158d626cc67115f7e1a8bd6f8adc7884e3 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 9 Aug 2024 17:47:58 +0000 Subject: [PATCH 4/6] Updated code formatting to match rules in .editorconfig --- .../Binders/CallBlockBinder.cs | 11 +++++----- .../Binders/CallStatementBinder.cs | 4 ++-- .../Binders/ForEachBlockBinder.cs | 7 +++--- .../Binders/PipeBlockBinder.cs | 5 ++--- .../Binders/PipeStatementBinder.cs | 10 ++++----- .../Binders/ReduceBlockBinder.cs | 9 ++++---- .../Binders/WaitAllBlockBinder.cs | 12 +++++----- .../Builders/CallBlockBuilder.cs | 2 +- .../Builders/ExpressionBinder.cs | 22 +++++++++---------- .../Builders/PipeIfBlockBuilder.cs | 4 ++-- .../Builders/PipeStatementBuilder.cs | 10 ++++----- src/Hyperbee.Pipeline/Builders/WrapBuilder.cs | 4 ++-- 12 files changed, 48 insertions(+), 52 deletions(-) diff --git a/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs index 9e857fa..20e591e 100644 --- a/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs @@ -1,8 +1,7 @@ using System.Linq.Expressions; -using Hyperbee.Pipeline.Context; using System.Reflection; - using Hyperbee.Pipeline.Binders.Abstractions; +using Hyperbee.Pipeline.Context; namespace Hyperbee.Pipeline.Binders; @@ -16,12 +15,12 @@ public CallBlockBinder( Expression> function ) public Expression> Bind( Expression> next ) { // Get the MethodInfo for the helper method - var bindImplAsyncMethodInfo = typeof(CallBlockBinder) - .GetMethod( nameof(BindImplAsync), BindingFlags.NonPublic | BindingFlags.Instance )!; + var bindImplAsyncMethodInfo = typeof( CallBlockBinder ) + .GetMethod( nameof( BindImplAsync ), BindingFlags.NonPublic | BindingFlags.Instance )!; // Create parameters for the lambda expression - var paramContext = Expression.Parameter( typeof(IPipelineContext), "context" ); - var paramArgument = Expression.Parameter( typeof(TInput), "argument" ); + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); // Create a call expression to the helper method var callBindImplAsync = Expression.Call( diff --git a/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs b/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs index a117db9..5f50770 100644 --- a/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs @@ -17,8 +17,8 @@ public Expression> Bind( ProcedureAsync var defaultName = (method ?? next.Method).Name; // Get the MethodInfo for the helper method - var bindImplAsyncMethodInfo = typeof( CallStatementBinder) - .GetMethod( nameof(BindImplAsync), BindingFlags.NonPublic | BindingFlags.Instance )!; + var bindImplAsyncMethodInfo = typeof( CallStatementBinder ) + .GetMethod( nameof( BindImplAsync ), BindingFlags.NonPublic | BindingFlags.Instance )!; // Create parameters for the lambda expression var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); diff --git a/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs index c6b59c6..14e9d32 100644 --- a/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs @@ -1,8 +1,7 @@ -using Hyperbee.Pipeline.Binders.Abstractions; - -using System.Linq.Expressions; -using Hyperbee.Pipeline.Context; +using System.Linq.Expressions; using System.Reflection; +using Hyperbee.Pipeline.Binders.Abstractions; +using Hyperbee.Pipeline.Context; namespace Hyperbee.Pipeline.Binders; diff --git a/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs index 6607863..a2fbdd7 100644 --- a/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs @@ -1,7 +1,6 @@ -using Hyperbee.Pipeline.Binders.Abstractions; - -using System.Linq.Expressions; +using System.Linq.Expressions; using System.Reflection; +using Hyperbee.Pipeline.Binders.Abstractions; using Hyperbee.Pipeline.Context; namespace Hyperbee.Pipeline.Binders; diff --git a/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs b/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs index d785957..37fe019 100644 --- a/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs @@ -19,13 +19,13 @@ public Expression> Bind( FunctionAsync) - .GetMethod( nameof(BindImplAsync), BindingFlags.NonPublic | BindingFlags.Instance )! - .MakeGenericMethod( typeof(TNext) ); + var bindImplAsyncMethodInfo = typeof( PipeStatementBinder ) + .GetMethod( nameof( BindImplAsync ), BindingFlags.NonPublic | BindingFlags.Instance )! + .MakeGenericMethod( typeof( TNext ) ); // Create parameters for the lambda expression - var paramContext = Expression.Parameter( typeof(IPipelineContext), "context" ); - var paramArgument = Expression.Parameter( typeof(TInput), "argument" ); + var paramContext = Expression.Parameter( typeof( IPipelineContext ), "context" ); + var paramArgument = Expression.Parameter( typeof( TInput ), "argument" ); // Create a call expression to the helper method var callBindImplAsync = Expression.Call( diff --git a/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs index dadf350..535bf0f 100644 --- a/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs @@ -1,8 +1,7 @@ -using Hyperbee.Pipeline.Binders.Abstractions; - -using System.Linq.Expressions; -using Hyperbee.Pipeline.Context; +using System.Linq.Expressions; using System.Reflection; +using Hyperbee.Pipeline.Binders.Abstractions; +using Hyperbee.Pipeline.Context; namespace Hyperbee.Pipeline.Binders; @@ -17,7 +16,7 @@ public ReduceBlockBinder( Func reducer, Expression> Bind( Expression> next ) - { + { // Get the MethodInfo for the BindImpl method var bindImplAsyncMethodInfo = typeof( ReduceBlockBinder ) .GetMethod( nameof( BindImplAsync ), BindingFlags.NonPublic | BindingFlags.Instance )!; diff --git a/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs index 3857b04..3ecac3d 100644 --- a/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs @@ -1,6 +1,6 @@ -using Hyperbee.Pipeline.Binders.Abstractions; -using System.Linq.Expressions; +using System.Linq.Expressions; using System.Reflection; +using Hyperbee.Pipeline.Binders.Abstractions; using Hyperbee.Pipeline.Context; using Hyperbee.Pipeline.Extensions; using Hyperbee.Pipeline.Extensions.Implementation; @@ -11,16 +11,16 @@ internal class WaitAllBlockBinder : ConditionalBlockBinder Middleware { get; } - public WaitAllBlockBinder( - Expression> function, - MiddlewareAsync middleware, + public WaitAllBlockBinder( + Expression> function, + MiddlewareAsync middleware, Action configure ) : base( null, function, configure ) { Middleware = middleware; } - public WaitAllBlockBinder( + public WaitAllBlockBinder( Function condition, Expression> function, MiddlewareAsync middleware, diff --git a/src/Hyperbee.Pipeline/Builders/CallBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/CallBlockBuilder.cs index 4bf898c..9c7cba3 100644 --- a/src/Hyperbee.Pipeline/Builders/CallBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/CallBlockBuilder.cs @@ -24,7 +24,7 @@ public IPipelineBuilder Call( bool inheritMiddleware, ArgumentNullException.ThrowIfNull( builder ); var block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ); - var function = builder( block ).CastFunction(); // cast because we don't know the final Pipe output value + var function = builder( block ).CastFunction(); // cast because we don't know the final Pipe output value return new PipelineBuilder { diff --git a/src/Hyperbee.Pipeline/Builders/ExpressionBinder.cs b/src/Hyperbee.Pipeline/Builders/ExpressionBinder.cs index 569cd89..610145b 100644 --- a/src/Hyperbee.Pipeline/Builders/ExpressionBinder.cs +++ b/src/Hyperbee.Pipeline/Builders/ExpressionBinder.cs @@ -10,24 +10,24 @@ public static Expression> ToExpression( Action action(context); + return ( context ) => action( context ); } - public static Expression> ToExpression( Func func ) => + public static Expression> ToExpression( Func func ) => input => func( input ); - public static Expression> ToExpression( Function function ) => - (context, argument) => function(context, argument); + public static Expression> ToExpression( Function function ) => + ( context, argument ) => function( context, argument ); - public static Expression> ToExpression( Procedure function ) => - (context, argument) => function(context, argument); + public static Expression> ToExpression( Procedure function ) => + ( context, argument ) => function( context, argument ); - public static Expression> ToExpression( FunctionAsync function ) => + public static Expression> ToExpression( FunctionAsync function ) => ( context, input ) => function( context, input ); - public static Expression> ToExpression( ProcedureAsync function ) => - (context, argument) => function(context, argument); + public static Expression> ToExpression( ProcedureAsync function ) => + ( context, argument ) => function( context, argument ); - public static Expression> ToExpression( MiddlewareAsync middleware ) => - (context, argument, next) => middleware(context, argument, next); + public static Expression> ToExpression( MiddlewareAsync middleware ) => + ( context, argument, next ) => middleware( context, argument, next ); } diff --git a/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs index 91d8922..af837d3 100644 --- a/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs @@ -25,8 +25,8 @@ public IPipelineBuilder PipeIf( Function co return new PipelineBuilder { - Function = new PipeIfBlockBinder( - condition, + Function = new PipeIfBlockBinder( + condition, Function ).Bind( function ), Middleware = Middleware }; diff --git a/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs b/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs index c9f8ce5..f752193 100644 --- a/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs @@ -21,8 +21,8 @@ public IPipelineBuilder Pipe( Function nex return new PipelineBuilder { - Function = new PipeStatementBinder( - Function, + Function = new PipeStatementBinder( + Function, Middleware, config ).Bind( AsyncNext, next.Method ), Middleware = Middleware @@ -44,9 +44,9 @@ public IPipelineBuilder PipeAsync( FunctionAsync { - Function = new PipeStatementBinder( - Function, - Middleware, + Function = new PipeStatementBinder( + Function, + Middleware, config ).Bind( next ), Middleware = Middleware }; diff --git a/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs b/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs index cf5af17..d8167fd 100644 --- a/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs @@ -21,8 +21,8 @@ public IPipelineBuilder WrapAsync( MiddlewareAsync { - Function = new WrapBinder( - ExpressionBinder.ToExpression( pipelineMiddleware ), + Function = new WrapBinder( + ExpressionBinder.ToExpression( pipelineMiddleware ), config ).Bind( Function ), Middleware = Middleware }; From dfbdc794bab987eb01cdde3b3f8afba79a9926e4 Mon Sep 17 00:00:00 2001 From: Matt Edwards Date: Fri, 9 Aug 2024 15:27:41 -0400 Subject: [PATCH 5/6] clean up --- src/Hyperbee.Pipeline/Binders/HookBinder.cs | 4 +--- src/Hyperbee.Pipeline/Binders/WrapBinder.cs | 6 +++--- .../Builders/CallBlockBuilder.cs | 3 +-- .../Builders/CallIfBlockBuilder.cs | 3 +-- .../Builders/ExpressionBinder.cs | 18 ------------------ .../Builders/ForEachBlockBuilder.cs | 3 +-- .../Builders/PipeIfBlockBuilder.cs | 4 +--- .../Builders/PipeStatementBuilder.cs | 10 ++-------- .../Builders/WaitAllBlockBuilder.cs | 1 - src/Hyperbee.Pipeline/Builders/WrapBuilder.cs | 4 +--- 10 files changed, 11 insertions(+), 45 deletions(-) diff --git a/src/Hyperbee.Pipeline/Binders/HookBinder.cs b/src/Hyperbee.Pipeline/Binders/HookBinder.cs index 12fc935..8ca720d 100644 --- a/src/Hyperbee.Pipeline/Binders/HookBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/HookBinder.cs @@ -1,6 +1,4 @@ -using static System.Net.Mime.MediaTypeNames; - -namespace Hyperbee.Pipeline.Binders; +namespace Hyperbee.Pipeline.Binders; internal class HookBinder // explicit Type Args due to usage { diff --git a/src/Hyperbee.Pipeline/Binders/WrapBinder.cs b/src/Hyperbee.Pipeline/Binders/WrapBinder.cs index 39daf58..cdab228 100644 --- a/src/Hyperbee.Pipeline/Binders/WrapBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/WrapBinder.cs @@ -7,10 +7,10 @@ namespace Hyperbee.Pipeline.Binders; internal class WrapBinder { - private Expression> Middleware { get; } + private MiddlewareAsync Middleware { get; } private Action Configure { get; } - public WrapBinder( Expression> middleware, Action configure ) + public WrapBinder( MiddlewareAsync middleware, Action configure ) { Middleware = middleware; Configure = configure; @@ -31,7 +31,7 @@ public Expression> Bind( Expression Call( bool inheritMiddleware, return new PipelineBuilder { - Function = - new CallBlockBinder( Function ).Bind( ExpressionBinder.ToExpression( function ) ), + Function = new CallBlockBinder( Function ).Bind( ExpressionBinder.ToExpression( function ) ), Middleware = Middleware }; } diff --git a/src/Hyperbee.Pipeline/Builders/CallIfBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/CallIfBlockBuilder.cs index ce571af..57cf811 100644 --- a/src/Hyperbee.Pipeline/Builders/CallIfBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/CallIfBlockBuilder.cs @@ -21,8 +21,7 @@ public IPipelineBuilder CallIf( Function conditi ArgumentNullException.ThrowIfNull( condition ); var block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ); - //var function = builder( block ).CastFunction(); // cast because we don't know the final Pipe output value - var function = ((PipelineBuilder) builder( block )).Function; + var function = ((PipelineBuilder) builder( block )).Function; // cast because we don't know the final Pipe output value return new PipelineBuilder { diff --git a/src/Hyperbee.Pipeline/Builders/ExpressionBinder.cs b/src/Hyperbee.Pipeline/Builders/ExpressionBinder.cs index 610145b..6b5b8dc 100644 --- a/src/Hyperbee.Pipeline/Builders/ExpressionBinder.cs +++ b/src/Hyperbee.Pipeline/Builders/ExpressionBinder.cs @@ -1,27 +1,9 @@ using System.Linq.Expressions; -using Hyperbee.Pipeline.Context; namespace Hyperbee.Pipeline; public static class ExpressionBinder { - public static Expression> ToExpression( Action action ) - { - if ( action == null ) - return null; - - return ( context ) => action( context ); - } - - public static Expression> ToExpression( Func func ) => - input => func( input ); - - public static Expression> ToExpression( Function function ) => - ( context, argument ) => function( context, argument ); - - public static Expression> ToExpression( Procedure function ) => - ( context, argument ) => function( context, argument ); - public static Expression> ToExpression( FunctionAsync function ) => ( context, input ) => function( context, input ); diff --git a/src/Hyperbee.Pipeline/Builders/ForEachBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/ForEachBlockBuilder.cs index 1789449..eac398a 100644 --- a/src/Hyperbee.Pipeline/Builders/ForEachBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/ForEachBlockBuilder.cs @@ -26,8 +26,7 @@ public IPipelineBuilder ForEachAsync( bool inheritMid return new PipelineBuilder { - Function = new ForEachBlockBinder( Function ).Bind( - ExpressionBinder.ToExpression( function ) ), + Function = new ForEachBlockBinder( Function ).Bind( ExpressionBinder.ToExpression( function ) ), Middleware = Middleware }; } diff --git a/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs index af837d3..311391d 100644 --- a/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs @@ -25,9 +25,7 @@ public IPipelineBuilder PipeIf( Function co return new PipelineBuilder { - Function = new PipeIfBlockBinder( - condition, - Function ).Bind( function ), + Function = new PipeIfBlockBinder( condition, Function ).Bind( function ), Middleware = Middleware }; } diff --git a/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs b/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs index f752193..146f1da 100644 --- a/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs @@ -21,10 +21,7 @@ public IPipelineBuilder Pipe( Function nex return new PipelineBuilder { - Function = new PipeStatementBinder( - Function, - Middleware, - config ).Bind( AsyncNext, next.Method ), + Function = new PipeStatementBinder( Function, Middleware, config ).Bind( AsyncNext, next.Method ), Middleware = Middleware }; @@ -44,10 +41,7 @@ public IPipelineBuilder PipeAsync( FunctionAsync { - Function = new PipeStatementBinder( - Function, - Middleware, - config ).Bind( next ), + Function = new PipeStatementBinder( Function, Middleware, config ).Bind( next ), Middleware = Middleware }; } diff --git a/src/Hyperbee.Pipeline/Builders/WaitAllBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/WaitAllBlockBuilder.cs index 28166ed..8496c38 100644 --- a/src/Hyperbee.Pipeline/Builders/WaitAllBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/WaitAllBlockBuilder.cs @@ -58,7 +58,6 @@ public IPipelineBuilder WaitAll( var functions = builderInstances .Select( builder => new { builder, block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ) } ) - //.Select( x => ((PipelineBuilder) x.builder( x.block )).Function ) .Select( x => x.builder( x.block ).CastFunction() ) .ToArray(); diff --git a/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs b/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs index d8167fd..8e292e4 100644 --- a/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs @@ -21,9 +21,7 @@ public IPipelineBuilder WrapAsync( MiddlewareAsync { - Function = new WrapBinder( - ExpressionBinder.ToExpression( pipelineMiddleware ), - config ).Bind( Function ), + Function = new WrapBinder( pipelineMiddleware, config ).Bind( Function ), Middleware = Middleware }; } From dbd30c49ecfc393aa8614816c6e31998c9c87224 Mon Sep 17 00:00:00 2001 From: Matt Edwards Date: Fri, 9 Aug 2024 17:21:31 -0400 Subject: [PATCH 6/6] clean up benchmarks --- src/Hyperbee.Pipeline/PipelineBuilder.cs | 11 ++- .../PipelineBenchmarks.cs | 76 ++++++++++--------- 2 files changed, 49 insertions(+), 38 deletions(-) diff --git a/src/Hyperbee.Pipeline/PipelineBuilder.cs b/src/Hyperbee.Pipeline/PipelineBuilder.cs index 3b43199..9669c38 100644 --- a/src/Hyperbee.Pipeline/PipelineBuilder.cs +++ b/src/Hyperbee.Pipeline/PipelineBuilder.cs @@ -1,4 +1,4 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; using Hyperbee.Pipeline.Data; namespace Hyperbee.Pipeline; @@ -15,11 +15,12 @@ internal PipelineBuilder() public FunctionAsync Build() { // build and return the outermost method + var compiledPipeline = Function.Compile(); + return async ( context, argument ) => { try { - var compiledPipeline = Function.Compile(); var result = await compiledPipeline( context, argument ).ConfigureAwait( false ); if ( context.CancellationToken.IsCancellationRequested ) @@ -41,12 +42,13 @@ public FunctionAsync Build() public ProcedureAsync BuildAsProcedure() { + var compiledPipeline = Function.Compile(); + // build and return the outermost method return async ( context, argument ) => { try { - var compiledPipeline = Function.Compile(); await compiledPipeline( context, argument ).ConfigureAwait( false ); } catch ( Exception ex ) @@ -61,9 +63,10 @@ public ProcedureAsync BuildAsProcedure() FunctionAsync IPipelineBuilder.CastFunction() { + var compiledPipeline = Function.Compile(); + return async ( context, argument ) => { - var compiledPipeline = Function.Compile(); var result = await compiledPipeline( context, Cast( argument ) ).ConfigureAwait( false ); return Cast( result ); }; diff --git a/test/Hyperbee.Pipleline.Benchmark/PipelineBenchmarks.cs b/test/Hyperbee.Pipleline.Benchmark/PipelineBenchmarks.cs index f39bc7b..bd00d9e 100644 --- a/test/Hyperbee.Pipleline.Benchmark/PipelineBenchmarks.cs +++ b/test/Hyperbee.Pipleline.Benchmark/PipelineBenchmarks.cs @@ -12,34 +12,26 @@ namespace Hyperbee.Pipeline.Benchmark; public class PipelineBenchmarks { - - [Benchmark] - public void PipelineExecution() + private FunctionAsync _commandExecution; + private FunctionAsync _commandMiddleware; + private FunctionAsync _commandEnumeration; + private FunctionAsync _commandCancellation; + private FunctionAsync _commandAuth; + + [GlobalSetup] + public void Setup() { - var command = PipelineFactory + _commandExecution = PipelineFactory .Start() .Pipe( ( ctx, arg ) => int.Parse( arg ) ) .Build(); - command( new PipelineContext(), "5" ); - } - - [Benchmark] - public void PipelineMiddleware() - { - - var command = PipelineFactory + _commandMiddleware = PipelineFactory .Start() .HookAsync( async ( ctx, arg, next ) => await next( ctx, arg + "{" ) + "}" ) .Pipe( ( ctx, arg ) => arg + "1" ) .Build(); - command( new PipelineContext() ); - } - - [Benchmark] - public void PipelineEnumeration() - { var count = 0; var command1 = PipelineFactory @@ -48,7 +40,7 @@ public void PipelineEnumeration() .Pipe( ( ctx, arg ) => count += 10 ) .Build(); - var command = PipelineFactory + _commandEnumeration = PipelineFactory .Start() .Pipe( ( ctx, arg ) => arg.Split( ' ' ) ) .PipeAsync( async ( ctx, arg ) => @@ -61,13 +53,7 @@ public void PipelineEnumeration() .Pipe( ( ctx, arg ) => string.Join( ' ', arg ) ) .Build(); - command( new PipelineContext(), "e f" ); - } - - [Benchmark] - public void PipelineCancellation() - { - var command = PipelineFactory + _commandCancellation = PipelineFactory .Start() .Pipe( ( ctx, arg ) => 1 ) .Pipe( ( ctx, arg ) => @@ -78,7 +64,35 @@ public void PipelineCancellation() .Pipe( ( ctx, arg ) => 3 ) .Build(); - command( new PipelineContext() ); + _commandAuth = PipelineFactory + .Start() + .PipeIfClaim( new Claim( "Role", "reader" ), b => b.Pipe( Complex ) ) + .Build(); + + } + + [Benchmark] + public void PipelineExecution() + { + _commandExecution( new PipelineContext(), "5" ); + } + + [Benchmark] + public void PipelineMiddleware() + { + _commandMiddleware( new PipelineContext() ); + } + + [Benchmark] + public void PipelineEnumeration() + { + _commandEnumeration( new PipelineContext(), "e f" ); + } + + [Benchmark] + public void PipelineCancellation() + { + _commandCancellation( new PipelineContext() ); } [Benchmark] @@ -87,13 +101,7 @@ public void PipelineAuth() var factory = CreateContextFactory(); ILogger logger = null!; - - var command = PipelineFactory - .Start() - .PipeIfClaim( new Claim( "Role", "reader" ), b => b.Pipe( Complex ) ) - .Build(); - - command( factory.Create( logger ), "reader" ); + _commandAuth( factory.Create( logger ), "reader" ); } [Benchmark]