From 5c970a9b5418bde8b501f5a7db2a575f9c3cb3c7 Mon Sep 17 00:00:00 2001 From: bfarmer67 Date: Fri, 9 Aug 2024 12:52:31 -0700 Subject: [PATCH 1/2] Create Release 1.2.0 (#36) * [FEATURE]: Binder base (#31) * Implement abstract binders * Refactor existing binders to use abstract base --------- Co-authored-by: Brenton Farmer * [FEATURE]: Implement dedicated builder and binder for CallIfBlock and PipeIfBlock (#33) * Factor CallIfBlock* and PipeIfBlock* into their own builders and binders. --------- Co-authored-by: Brenton Farmer * [FEATURE]: Improve builder pattern (#35) * Refactor builders to eliminate reliance on partial interfaces and classes --------- Co-authored-by: Brenton Farmer * Fix footer * Previous version was 'v1.1.6'. Version now 'v1.2.0'. --------- Co-authored-by: Brenton Farmer --- Directory.Build.props | 4 +- Hyperbee.Pipeline.sln | 2 - docs/_includes/nav_footer_custom.html | 2 +- docs/syntax.md | 17 ++- .../Binders/Abstractions/Binder.cs | 26 ++++ .../Binders/Abstractions/BlockBinder.cs | 20 +++ .../Abstractions/ConditionalBlockBinder.cs | 31 ++++ .../Binders/Abstractions/StatementBinder.cs | 31 ++++ .../Binders/CallBlockBinder.cs | 24 ++-- .../Binders/CallIfBlockBinder.cs | 25 ++++ .../Binders/CallStatementBinder.cs | 50 ++----- .../Binders/ForEachBlockBinder.cs | 20 ++- src/Hyperbee.Pipeline/Binders/HookBinder.cs | 8 +- .../Binders/PipeBlockBinder.cs | 25 ++-- .../Binders/PipeIfBlockBinder.cs | 24 ++++ .../Binders/PipeStatementBinder.cs | 36 +---- .../Binders/ReduceBlockBinder.cs | 20 ++- .../Binders/WaitAllBlockBinder.cs | 68 ++++----- src/Hyperbee.Pipeline/Binders/WrapBinder.cs | 15 +- .../Builders/CallBlockBuilder.cs | 63 ++++----- .../Builders/CallIfBlockBuilder.cs | 50 +++++++ .../Builders/CallStatementBuilder.cs | 74 +++++++--- .../Builders/ForEachBlockBuilder.cs | 42 ++++-- src/Hyperbee.Pipeline/Builders/HookBuilder.cs | 46 ++++-- .../Builders/PipeBlockBuilder.cs | 63 ++++----- .../Builders/PipeIfBlockBuilder.cs | 51 +++++++ .../Builders/PipeStatementBuilder.cs | 72 +++++++--- .../Builders/ReduceBlockBuilder.cs | 49 +++++-- .../Builders/WaitAllBlockBuilder.cs | 83 +++++++---- src/Hyperbee.Pipeline/Builders/WrapBuilder.cs | 62 ++++++-- .../Context/IPipelineContext.cs | 3 +- .../Hyperbee.Pipeline.csproj | 5 +- src/Hyperbee.Pipeline/IPipelineBuilder.cs | 4 +- src/Hyperbee.Pipeline/IPipelineFunction.cs | 10 +- src/Hyperbee.Pipeline/PipelineBuilder.cs | 10 +- src/Hyperbee.Pipeline/README.md | 133 ------------------ .../PipelineEnumerationTests.cs | 4 +- 37 files changed, 766 insertions(+), 506 deletions(-) create mode 100644 src/Hyperbee.Pipeline/Binders/Abstractions/Binder.cs create mode 100644 src/Hyperbee.Pipeline/Binders/Abstractions/BlockBinder.cs create mode 100644 src/Hyperbee.Pipeline/Binders/Abstractions/ConditionalBlockBinder.cs create mode 100644 src/Hyperbee.Pipeline/Binders/Abstractions/StatementBinder.cs create mode 100644 src/Hyperbee.Pipeline/Binders/CallIfBlockBinder.cs create mode 100644 src/Hyperbee.Pipeline/Binders/PipeIfBlockBinder.cs create mode 100644 src/Hyperbee.Pipeline/Builders/CallIfBlockBuilder.cs create mode 100644 src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs delete mode 100644 src/Hyperbee.Pipeline/README.md diff --git a/Directory.Build.props b/Directory.Build.props index a4d7ef9..02c6fa8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,8 +2,8 @@ 1 - 1 - 6 + 2 + 0 diff --git a/Hyperbee.Pipeline.sln b/Hyperbee.Pipeline.sln index 6ba5ee6..fe21bb7 100644 --- a/Hyperbee.Pipeline.sln +++ b/Hyperbee.Pipeline.sln @@ -7,8 +7,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets - LICENSE = LICENSE - README.md = README.md solution-helper.psm1 = solution-helper.psm1 EndProjectSection EndProject diff --git a/docs/_includes/nav_footer_custom.html b/docs/_includes/nav_footer_custom.html index 7bb117a..3f2febb 100644 --- a/docs/_includes/nav_footer_custom.html +++ b/docs/_includes/nav_footer_custom.html @@ -1,3 +1,3 @@ 
- Hyperbee Json Docs + Hyperbee Pipeline Docs
\ No newline at end of file diff --git a/docs/syntax.md b/docs/syntax.md index a449bd7..ead930b 100644 --- a/docs/syntax.md +++ b/docs/syntax.md @@ -101,7 +101,7 @@ var count = 0; var command = PipelineFactory .Start() .Pipe( ( ctx, arg ) => arg.Split( ' ' ) ) - .ForEach( builder => builder + .ForEach().Type( builder => builder .Pipe( ( ctx, arg ) => count += 10 ) ) .Pipe( ( ctx, arg ) => count += 5 ) @@ -117,6 +117,21 @@ Assert.AreEqual( count, 25 ); `Reduce` and `ReduceAync` allow you to transform an enumerable pipeline input to a single value. You can specify a reducer function that defines how the elements should be combined, and a builder function that creates the pipeline for processing the elements.### Cancel +```csharp +var command = PipelineFactory + .Start() + .Pipe( ( ctx, arg ) => arg.Split( ' ' ) ) + .Reduce().Type( ( aggregate, value ) => aggregate + value, builder => builder + .Pipe( ( ctx, arg ) => int.Parse( arg ) + 10 ) + ) + .Pipe( ( ctx, arg ) => arg + 5 ) + .Build(); + +var result = await command( new PipelineContext(), "1 2 3 4 5" ); + +Assert.AreEqual( result, 70 ); +``` + ### WaitAll `WaitAll` allows you to wait for concurrent pipelines to complete before continuing. You can specify a set of builders that create diff --git a/src/Hyperbee.Pipeline/Binders/Abstractions/Binder.cs b/src/Hyperbee.Pipeline/Binders/Abstractions/Binder.cs new file mode 100644 index 0000000..eb97279 --- /dev/null +++ b/src/Hyperbee.Pipeline/Binders/Abstractions/Binder.cs @@ -0,0 +1,26 @@ +using Hyperbee.Pipeline.Context; +using Hyperbee.Pipeline.Extensions.Implementation; + +namespace Hyperbee.Pipeline.Binders.Abstractions; + +internal abstract class Binder +{ + protected FunctionAsync Pipeline { get; } + protected Action Configure { get; } + + protected Binder( FunctionAsync function, Action configure ) + { + Pipeline = function; + Configure = configure; + } + + protected virtual async Task<(TOutput Result, bool Canceled)> ProcessPipelineAsync( IPipelineContext context, TInput argument ) + { + var result = await Pipeline( context, argument ).ConfigureAwait( false ); + + var contextControl = (IPipelineContextControl) context; + var canceled = contextControl.HandleCancellationRequested( result ); + + return (canceled ? default : result, canceled); + } +} diff --git a/src/Hyperbee.Pipeline/Binders/Abstractions/BlockBinder.cs b/src/Hyperbee.Pipeline/Binders/Abstractions/BlockBinder.cs new file mode 100644 index 0000000..834d976 --- /dev/null +++ b/src/Hyperbee.Pipeline/Binders/Abstractions/BlockBinder.cs @@ -0,0 +1,20 @@ +using Hyperbee.Pipeline.Context; + +namespace Hyperbee.Pipeline.Binders.Abstractions; + +internal abstract class BlockBinder : Binder +{ + protected BlockBinder( FunctionAsync function, Action configure ) + : base( function, configure ) + { + } + + // Using TArgument instead of TOutput allows more capabilities for special + // use cases where the next argument is not the same as the output type + // like ReduceBlockBinder and ForEachBlockBinder + + protected virtual async Task ProcessBlockAsync( FunctionAsync blockFunction, IPipelineContext context, TArgument nextArgument ) + { + return await blockFunction( context, nextArgument ).ConfigureAwait( false ); + } +} diff --git a/src/Hyperbee.Pipeline/Binders/Abstractions/ConditionalBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/Abstractions/ConditionalBlockBinder.cs new file mode 100644 index 0000000..6f23e81 --- /dev/null +++ b/src/Hyperbee.Pipeline/Binders/Abstractions/ConditionalBlockBinder.cs @@ -0,0 +1,31 @@ +using System.Runtime.CompilerServices; +using Hyperbee.Pipeline.Context; + +namespace Hyperbee.Pipeline.Binders.Abstractions; + +internal abstract class ConditionalBlockBinder : BlockBinder +{ + protected Function Condition { get; } + + protected ConditionalBlockBinder( Function condition, FunctionAsync function, Action configure ) + : base( function, configure ) + { + Condition = condition; + } + + protected override async Task ProcessBlockAsync( FunctionAsync blockFunction, IPipelineContext context, TArgument nextArgument ) + { + if ( Condition != null && !Condition( context, CastTypeArg( nextArgument ) ) ) + { + return CastTypeArg( nextArgument ); + } + + return await base.ProcessBlockAsync( blockFunction, context, nextArgument ).ConfigureAwait( false ); + } + + [MethodImpl( MethodImplOptions.AggressiveInlining )] + private static TResult CastTypeArg( TType input ) + { + return (TResult) (object) input; + } +} diff --git a/src/Hyperbee.Pipeline/Binders/Abstractions/StatementBinder.cs b/src/Hyperbee.Pipeline/Binders/Abstractions/StatementBinder.cs new file mode 100644 index 0000000..b5fb44c --- /dev/null +++ b/src/Hyperbee.Pipeline/Binders/Abstractions/StatementBinder.cs @@ -0,0 +1,31 @@ +using Hyperbee.Pipeline.Context; +using Hyperbee.Pipeline.Extensions.Implementation; + +namespace Hyperbee.Pipeline.Binders.Abstractions; + +internal abstract class StatementBinder : Binder +{ + protected MiddlewareAsync Middleware { get; } + + protected StatementBinder( FunctionAsync function, MiddlewareAsync middleware, Action configure ) + : base( function, configure ) + { + Middleware = middleware; + } + + protected virtual async Task ProcessStatementAsync( FunctionAsync nextFunction, IPipelineContext context, TOutput nextArgument, string frameName ) + { + var contextControl = (IPipelineContextControl) context; + + using var _ = contextControl.CreateFrame( context, Configure, frameName ); + + if ( Middleware == null ) + return await nextFunction( context, nextArgument ).ConfigureAwait( false ); + + return (TNext) await Middleware( + context, + nextArgument, + async ( context1, argument1 ) => await nextFunction( context1, (TOutput) argument1 ).ConfigureAwait( false ) + ).ConfigureAwait( false ); + } +} diff --git a/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs index cdbafe1..75ebd4b 100644 --- a/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs @@ -1,30 +1,24 @@ -namespace Hyperbee.Pipeline.Binders; +using Hyperbee.Pipeline.Binders.Abstractions; -internal class CallBlockBinder -{ - private FunctionAsync Pipeline { get; } - private Function Condition { get; } +namespace Hyperbee.Pipeline.Binders; +internal class CallBlockBinder : BlockBinder +{ public CallBlockBinder( FunctionAsync function ) - : this( null, function ) - { - } - - public CallBlockBinder( Function condition, FunctionAsync function ) + : base( function, default ) { - Condition = condition; - Pipeline = function; } public FunctionAsync Bind( FunctionAsync next ) { return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false ); - if ( Condition == null || Condition( context, nextArgument ) ) - await next( context, nextArgument ).ConfigureAwait( false ); + if ( canceled ) + return default; + 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 new file mode 100644 index 0000000..55494c8 --- /dev/null +++ b/src/Hyperbee.Pipeline/Binders/CallIfBlockBinder.cs @@ -0,0 +1,25 @@ +using Hyperbee.Pipeline.Binders.Abstractions; + +namespace Hyperbee.Pipeline.Binders; + +internal class CallIfBlockBinder : ConditionalBlockBinder +{ + public CallIfBlockBinder( Function condition, FunctionAsync function ) + : base( condition, function, default ) + { + } + + public FunctionAsync Bind( FunctionAsync next ) + { + return async ( context, argument ) => + { + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).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 36ad071..ab4f296 100644 --- a/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/CallStatementBinder.cs @@ -1,20 +1,14 @@ using System.Reflection; +using Hyperbee.Pipeline.Binders.Abstractions; using Hyperbee.Pipeline.Context; -using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline.Binders; -internal class CallStatementBinder +internal class CallStatementBinder : StatementBinder { - private FunctionAsync Pipeline { get; } - private MiddlewareAsync Middleware { get; } - private Action Configure { get; } - public CallStatementBinder( FunctionAsync function, MiddlewareAsync middleware, Action configure ) + : base( function, middleware, configure ) { - Pipeline = function; - Middleware = middleware; - Configure = configure; } public FunctionAsync Bind( ProcedureAsync next, MethodInfo method = null ) @@ -23,38 +17,18 @@ public FunctionAsync Bind( ProcedureAsync next, Method return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); - - var contextControl = (IPipelineContextControl) context; + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false ); - if ( contextControl.HandleCancellationRequested( nextArgument ) ) + if ( canceled ) return default; - using ( contextControl.CreateFrame( context, Configure, defaultName ) ) - { - return await Next( next, context, nextArgument ).ConfigureAwait( false ); - } + return await ProcessStatementAsync( + async ( ctx, arg ) => + { + await next( ctx, arg ).ConfigureAwait( false ); + return arg; + }, context, nextArgument, defaultName ).ConfigureAwait( false ); }; } - - private async Task Next( ProcedureAsync next, IPipelineContext context, TOutput nextArgument ) - { - if ( Middleware == null ) - { - await next( context, nextArgument ).ConfigureAwait( false ); - return nextArgument; - } - - await Middleware( - context, - nextArgument, - async ( context1, argument1 ) => - { - await next( context1, (TOutput) argument1 ).ConfigureAwait( false ); - return nextArgument; - } - ).ConfigureAwait( false ); - - return nextArgument; - } } + diff --git a/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs index f29fcb0..7c2b00a 100644 --- a/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/ForEachBlockBinder.cs @@ -1,27 +1,33 @@ -namespace Hyperbee.Pipeline.Binders; +using Hyperbee.Pipeline.Binders.Abstractions; -internal class ForEachBlockBinder -{ - private FunctionAsync Pipeline { get; } +namespace Hyperbee.Pipeline.Binders; +internal class ForEachBlockBinder : BlockBinder +{ public ForEachBlockBinder( FunctionAsync function ) + : base( function, default ) { - Pipeline = function; } public FunctionAsync Bind( FunctionAsync next ) + { return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false ); + + if ( canceled ) + return default; + var nextArguments = (IEnumerable) nextArgument; foreach ( var elementArgument in nextArguments ) { - await next( context, elementArgument ).ConfigureAwait( false ); + await ProcessBlockAsync( next, context, elementArgument ).ConfigureAwait( false ); } return nextArgument; }; } } + diff --git a/src/Hyperbee.Pipeline/Binders/HookBinder.cs b/src/Hyperbee.Pipeline/Binders/HookBinder.cs index c3be299..5ece4da 100644 --- a/src/Hyperbee.Pipeline/Binders/HookBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/HookBinder.cs @@ -1,15 +1,15 @@ namespace Hyperbee.Pipeline.Binders; -internal class HookBinder // explicit Type Args due to usage +internal class HookBinder // explicit Type Args due to usage { - private MiddlewareAsync Middleware { get; } + private MiddlewareAsync Middleware { get; } - public HookBinder( MiddlewareAsync middleware ) + public HookBinder( MiddlewareAsync middleware ) { Middleware = middleware ?? (async ( context, argument, next ) => await next( context, argument ).ConfigureAwait( false )); } - public MiddlewareAsync Bind( MiddlewareAsync middleware ) + public MiddlewareAsync Bind( MiddlewareAsync middleware ) { return async ( context, argument, function ) => await middleware( diff --git a/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs index d5acfe7..7b081a7 100644 --- a/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/PipeBlockBinder.cs @@ -1,31 +1,24 @@ -namespace Hyperbee.Pipeline.Binders; +using Hyperbee.Pipeline.Binders.Abstractions; -internal class PipeBlockBinder -{ - private FunctionAsync Pipeline { get; } - private Function Condition { get; } +namespace Hyperbee.Pipeline.Binders; +internal class PipeBlockBinder : BlockBinder +{ public PipeBlockBinder( FunctionAsync function ) - : this( null, function ) - { - } - - public PipeBlockBinder( Function condition, FunctionAsync function ) + : base( function, default ) { - Condition = condition; - Pipeline = function; } public FunctionAsync Bind( FunctionAsync next ) { return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false ); - if ( Condition == null || Condition( context, nextArgument ) ) - return await next( context, nextArgument ).ConfigureAwait( false ); + if ( canceled ) + return default; - return (TNext) (object) nextArgument; + return await ProcessBlockAsync( next, context, nextArgument ).ConfigureAwait( false ); }; } } diff --git a/src/Hyperbee.Pipeline/Binders/PipeIfBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/PipeIfBlockBinder.cs new file mode 100644 index 0000000..16219d1 --- /dev/null +++ b/src/Hyperbee.Pipeline/Binders/PipeIfBlockBinder.cs @@ -0,0 +1,24 @@ +using Hyperbee.Pipeline.Binders.Abstractions; + +namespace Hyperbee.Pipeline.Binders; + +internal class PipeIfBlockBinder : ConditionalBlockBinder +{ + public PipeIfBlockBinder( Function condition, FunctionAsync function ) + : base( condition, function, default ) + { + } + + public FunctionAsync Bind( FunctionAsync next ) + { + return async ( context, argument ) => + { + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).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 228acda..cfdb841 100644 --- a/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/PipeStatementBinder.cs @@ -1,20 +1,14 @@ using System.Reflection; +using Hyperbee.Pipeline.Binders.Abstractions; using Hyperbee.Pipeline.Context; -using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline.Binders; -internal class PipeStatementBinder +internal class PipeStatementBinder : StatementBinder { - private FunctionAsync Pipeline { get; } - private MiddlewareAsync Middleware { get; } - private Action Configure { get; } - public PipeStatementBinder( FunctionAsync function, MiddlewareAsync middleware, Action configure ) + : base( function, middleware, configure ) { - Pipeline = function; - Middleware = middleware; - Configure = configure; } public FunctionAsync Bind( FunctionAsync next, MethodInfo method = null ) @@ -23,30 +17,12 @@ public FunctionAsync Bind( FunctionAsync n return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); - - var contextControl = (IPipelineContextControl) context; + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false ); - if ( contextControl.HandleCancellationRequested( nextArgument ) ) + if ( canceled ) return default; - using ( contextControl.CreateFrame( context, Configure, defaultName ) ) - { - return await Next( next, context, nextArgument ).ConfigureAwait( false ); - } + return await ProcessStatementAsync( next, context, nextArgument, defaultName ).ConfigureAwait( false ); }; } - - private async Task Next( FunctionAsync next, 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 0dbec9f..a184fb0 100644 --- a/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/ReduceBlockBinder.cs @@ -1,28 +1,33 @@ -namespace Hyperbee.Pipeline.Binders; +using Hyperbee.Pipeline.Binders.Abstractions; -internal class ReduceBlockBinder +namespace Hyperbee.Pipeline.Binders; + +internal class ReduceBlockBinder : BlockBinder { - private FunctionAsync Pipeline { get; } private Func Reducer { get; } public ReduceBlockBinder( Func reducer, FunctionAsync function ) + : base( function, default ) { Reducer = reducer; - Pipeline = function; } public FunctionAsync Bind( FunctionAsync next ) { return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); - var nextArguments = (IEnumerable) nextArgument; + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false ); + + if ( canceled ) + return default; + var nextArguments = (IEnumerable) nextArgument; var accumulator = default( TNext ); + // Process each element and apply the reducer foreach ( var elementArgument in nextArguments ) { - var result = await next( context, elementArgument ).ConfigureAwait( false ); + var result = await ProcessBlockAsync( next, context, elementArgument ).ConfigureAwait( false ); accumulator = Reducer( accumulator, result ); } @@ -30,3 +35,4 @@ public FunctionAsync Bind( FunctionAsync next ) }; } } + diff --git a/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs b/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs index 28dcd4b..e93eea9 100644 --- a/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/WaitAllBlockBinder.cs @@ -1,15 +1,14 @@ -using Hyperbee.Pipeline.Context; +using Hyperbee.Pipeline.Binders.Abstractions; +using Hyperbee.Pipeline.Context; using Hyperbee.Pipeline.Extensions; using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline.Binders; -internal class WaitAllBlockBinder + +internal class WaitAllBlockBinder : ConditionalBlockBinder { - private FunctionAsync Pipeline { get; } private MiddlewareAsync Middleware { get; } - private Action Configure { get; } - private Function Condition { get; } public WaitAllBlockBinder( FunctionAsync function, MiddlewareAsync middleware, Action configure ) : this( null, function, middleware, configure ) @@ -17,11 +16,9 @@ public WaitAllBlockBinder( FunctionAsync function, MiddlewareAs } public WaitAllBlockBinder( Function condition, FunctionAsync function, MiddlewareAsync middleware, Action configure ) + : base( condition, function, configure ) { - Condition = condition; - Pipeline = function; Middleware = middleware; - Configure = configure; } public FunctionAsync Bind( FunctionAsync[] nexts, WaitAllReducer reducer ) @@ -30,56 +27,49 @@ public FunctionAsync Bind( FunctionAsync[ return async ( context, argument ) => { - var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false ); - - if ( Condition != null && !Condition( context, nextArgument ) ) - return (TNext) (object) nextArgument; - - // mind cancellation and execute - var contextControl = (IPipelineContextControl) context; + var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false ); - if ( contextControl.HandleCancellationRequested( nextArgument ) ) + if ( canceled ) return default; - using ( contextControl.CreateFrame( context, Configure, nameof( WaitAllAsync ) ) ) - { - return await Next( WaitAllAsync, context, nextArgument ).ConfigureAwait( false ); - } - // 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. - async Task WaitAllAsync( IPipelineContext context1, TOutput _ ) - { - var results = new WaitAllResult[nexts.Length]; - var items = nexts.Select( ( x, i ) => new { next = x, index = i } ); + return await WaitAllAsync( context, nextArgument, nexts, reducer ).ConfigureAwait( false ); + }; + } - await items.ForEachAsync( async item => - { - var innerContext = context1.Clone( false ); // context fork + private async Task WaitAllAsync( IPipelineContext context, TOutput nextArgument, FunctionAsync[] nexts, WaitAllReducer reducer ) + { + var contextControl = (IPipelineContextControl) context; + using var _ = contextControl.CreateFrame( context, Configure, nameof( WaitAllAsync ) ); - var result = await item.next( innerContext, nextArgument ).ConfigureAwait( false ); + var results = new WaitAllResult[nexts.Length]; + var items = nexts.Select( ( x, i ) => new { next = x, index = i } ); - results[item.index] = new WaitAllResult { Context = innerContext, Result = result }; - } ) - .ConfigureAwait( false ); + await items.ForEachAsync( async item => + { + var innerContext = context.Clone( false ); // context fork - return reducer( context, nextArgument, results ); - } - }; + var result = await ProcessStatementAsync( item.next, innerContext, nextArgument ).ConfigureAwait( false ); + + results[item.index] = new WaitAllResult { Context = innerContext, Result = result }; + } ).ConfigureAwait( false ); + + return reducer( context, nextArgument, results ); } - private async Task Next( FunctionAsync waitAll, IPipelineContext context, TOutput nextArgument ) + private async Task ProcessStatementAsync( FunctionAsync next, IPipelineContext context, TOutput nextArgument ) { if ( Middleware == null ) - return await waitAll( context, nextArgument ).ConfigureAwait( false ); + return await next( context, nextArgument ).ConfigureAwait( false ); - return (TNext) await Middleware( + return await Middleware( context, nextArgument, - async ( context1, argument1 ) => await waitAll( context1, (TOutput) argument1 ).ConfigureAwait( false ) + async ( context1, argument1 ) => await next( context1, (TOutput) argument1 ).ConfigureAwait( false ) ).ConfigureAwait( false ); } } diff --git a/src/Hyperbee.Pipeline/Binders/WrapBinder.cs b/src/Hyperbee.Pipeline/Binders/WrapBinder.cs index c5c1af8..471738f 100644 --- a/src/Hyperbee.Pipeline/Binders/WrapBinder.cs +++ b/src/Hyperbee.Pipeline/Binders/WrapBinder.cs @@ -22,14 +22,13 @@ public FunctionAsync Bind( FunctionAsync next { var contextControl = (IPipelineContextControl) context; - using ( contextControl.CreateFrame( context, Configure, defaultName ) ) - { - return await Middleware( - context, - argument, - async ( context1, argument1 ) => await next( context1, argument1 ).ConfigureAwait( false ) - ).ConfigureAwait( false ); - } + using var _ = contextControl.CreateFrame( context, Configure, defaultName ); + + 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 b380f68..3d57e25 100644 --- a/src/Hyperbee.Pipeline/Builders/CallBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/CallBlockBuilder.cs @@ -1,58 +1,47 @@ using Hyperbee.Pipeline.Binders; +using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline; -public partial interface IPipelineBuilder +public static class CallBlockBuilder { - IPipelineBuilder Call( Func, IPipelineBuilder> builder ); - IPipelineBuilder Call( bool inheritMiddleware, Func, IPipelineBuilder> builder ); - IPipelineBuilder CallIf( Function condition, Func, IPipelineBuilder> builder ); - IPipelineBuilder CallIf( Function condition, bool inheritMiddleware, Func, IPipelineBuilder> builder ); - -} - -public partial class PipelineBuilder -{ - // Call an inner builder discarding the final result. Acts like an Action. - - public IPipelineBuilder Call( Func, IPipelineBuilder> builder ) + public static IPipelineBuilder Call( + this IPipelineBuilder parent, + Func, IPipelineBuilder> builder + ) { - return Call( true, builder ); + return CallBlockBuilder.Call( parent, true, builder ); } - public IPipelineBuilder Call( bool inheritMiddleware, Func, IPipelineBuilder> builder ) + public static IPipelineBuilder Call( + this IPipelineBuilder parent, + 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 - - return new PipelineBuilder - { - Function = new CallBlockBinder( Function ).Bind( function ), - Middleware = Middleware - }; - } - - // CallIf - - public IPipelineBuilder CallIf( Function condition, Func, IPipelineBuilder> builder ) - { - return CallIf( condition, true, builder ); + return CallBlockBuilder.Call( parent, inheritMiddleware, builder ); } +} - public IPipelineBuilder CallIf( Function condition, bool inheritMiddleware, Func, IPipelineBuilder> builder ) +internal static class CallBlockBuilder +{ + public static IPipelineBuilder Call( + IPipelineBuilder parent, + bool inheritMiddleware, + Func, IPipelineBuilder> builder + ) { ArgumentNullException.ThrowIfNull( builder ); - ArgumentNullException.ThrowIfNull( condition ); - var block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ); + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); + + var block = PipelineFactory.Start( inheritMiddleware ? parentMiddleware : null ); var function = builder( block ).CastFunction(); // cast because we don't know the final Pipe output value return new PipelineBuilder { - Function = new CallBlockBinder( condition, Function ).Bind( function ), - Middleware = Middleware + Function = new CallBlockBinder( parentFunction ).Bind( function ), + Middleware = parentMiddleware }; } } diff --git a/src/Hyperbee.Pipeline/Builders/CallIfBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/CallIfBlockBuilder.cs new file mode 100644 index 0000000..90b7993 --- /dev/null +++ b/src/Hyperbee.Pipeline/Builders/CallIfBlockBuilder.cs @@ -0,0 +1,50 @@ +using Hyperbee.Pipeline.Binders; +using Hyperbee.Pipeline.Extensions.Implementation; + +namespace Hyperbee.Pipeline; + +public static class CallIfBuilder +{ + public static IPipelineBuilder CallIf( + this IPipelineBuilder parent, + Function condition, + Func, IPipelineBuilder> builder + ) + { + return CallIfBuilder.CallIf( parent, condition, true, builder ); + } + public static IPipelineBuilder CallIf( + this IPipelineBuilder parent, + Function condition, + bool inheritMiddleware, + Func, IPipelineBuilder> builder + ) + { + return CallIfBuilder.CallIf( parent, condition, inheritMiddleware, builder ); + } +} + +internal static class CallIfBuilder +{ + public static IPipelineBuilder CallIf( + IPipelineBuilder parent, + Function condition, + bool inheritMiddleware, + Func, IPipelineBuilder> builder + ) + { + ArgumentNullException.ThrowIfNull( builder ); + ArgumentNullException.ThrowIfNull( condition ); + + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); + + var block = PipelineFactory.Start( inheritMiddleware ? parentMiddleware : null ); + var function = builder( block ).CastFunction(); // cast because we don't know the final Pipe output value + + return new PipelineBuilder + { + Function = new CallIfBlockBinder( condition, parentFunction ).Bind( function ), + Middleware = parentMiddleware + }; + } +} diff --git a/src/Hyperbee.Pipeline/Builders/CallStatementBuilder.cs b/src/Hyperbee.Pipeline/Builders/CallStatementBuilder.cs index f9c8e0c..e061065 100644 --- a/src/Hyperbee.Pipeline/Builders/CallStatementBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/CallStatementBuilder.cs @@ -1,28 +1,64 @@ -using Hyperbee.Pipeline.Binders; +using System.Xml.Linq; +using Hyperbee.Pipeline.Binders; using Hyperbee.Pipeline.Context; +using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline; -public partial interface IPipelineBuilder +public static class CallStatementBuilder { - IPipelineBuilder Call( Procedure next, string name ); - IPipelineBuilder Call( Procedure next, Action config = null ); - IPipelineBuilder CallAsync( ProcedureAsync next, string name ); - IPipelineBuilder CallAsync( ProcedureAsync next, Action config = null ); + public static IPipelineBuilder Call( + this IPipelineBuilder parent, + Procedure next, string name + ) + { + return CallStatementBuilder.Call( parent, next, config => config.Name = name ); + } + + public static IPipelineBuilder Call( + this IPipelineBuilder parent, + Procedure next, + Action config = null + ) + { + return CallStatementBuilder.Call( parent, next, config ); + } + + public static IPipelineBuilder CallAsync( + this IPipelineBuilder parent, + ProcedureAsync next, + string name + ) + { + return CallStatementBuilder.CallAsync( parent, next, config => config.Name = name ); + } + + public static IPipelineBuilder CallAsync( + this IPipelineBuilder parent, + ProcedureAsync next, + Action config = null + ) + { + return CallStatementBuilder.CallAsync( parent, next, config ); + } } -public partial class PipelineBuilder +internal static class CallStatementBuilder { - public IPipelineBuilder Call( Procedure next, string name ) => Call( next, config => config.Name = name ); - - public IPipelineBuilder Call( Procedure next, Action config = null ) + public static IPipelineBuilder Call( + IPipelineBuilder parent, + Procedure next, + Action config = null + ) { ArgumentNullException.ThrowIfNull( next ); + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); + return new PipelineBuilder { - Function = new CallStatementBinder( Function, Middleware, config ).Bind( AsyncNext, next.Method ), - Middleware = Middleware + Function = new CallStatementBinder( parentFunction, parentMiddleware, config ).Bind( AsyncNext, next.Method ), + Middleware = parentMiddleware }; // task wrapper @@ -34,16 +70,20 @@ Task AsyncNext( IPipelineContext context, TOutput argument ) } } - public IPipelineBuilder CallAsync( ProcedureAsync next, string name ) => CallAsync( next, config => config.Name = name ); - - public IPipelineBuilder CallAsync( ProcedureAsync next, Action config = null ) + public static IPipelineBuilder CallAsync( + IPipelineBuilder parent, + ProcedureAsync next, + Action config = null + ) { ArgumentNullException.ThrowIfNull( next ); + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); + return new PipelineBuilder { - Function = new CallStatementBinder( Function, Middleware, config ).Bind( next ), - Middleware = Middleware + Function = new CallStatementBinder( parentFunction, parentMiddleware, config ).Bind( next ), + Middleware = parentMiddleware }; } } diff --git a/src/Hyperbee.Pipeline/Builders/ForEachBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/ForEachBlockBuilder.cs index 384c1bf..2456159 100644 --- a/src/Hyperbee.Pipeline/Builders/ForEachBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/ForEachBlockBuilder.cs @@ -1,31 +1,49 @@ using Hyperbee.Pipeline.Binders; +using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline; -public partial interface IPipelineBuilder +public static class ForEachBlockBuilder { - IPipelineBuilder ForEach( Func, IPipelineBuilder> builder ); - IPipelineBuilder ForEachAsync( bool inheritMiddleware, Func, IPipelineBuilder> builder ); -} + public static ForEachBlockBuilderWrapper ForEach( this IPipelineBuilder parent ) + { + return new ForEachBlockBuilderWrapper( parent ); + } -public partial class PipelineBuilder -{ - public IPipelineBuilder ForEach( Func, IPipelineBuilder> builder ) + public class ForEachBlockBuilderWrapper( IPipelineBuilder parent ) { - return ForEachAsync( true, builder ); + public IPipelineBuilder Type( Func, IPipelineBuilder> builder ) + { + return ForEachBlockBuilder.ForEach( parent, true, builder ); + } + + public IPipelineBuilder Type( bool inheritMiddleware, Func, IPipelineBuilder> builder ) + { + return ForEachBlockBuilder.ForEach( parent, inheritMiddleware, builder ); + } } +} - public IPipelineBuilder ForEachAsync( bool inheritMiddleware, Func, IPipelineBuilder> builder ) +internal static class ForEachBlockBuilder +{ + public static IPipelineBuilder ForEach( + IPipelineBuilder parent, + bool inheritMiddleware, + Func, IPipelineBuilder> builder + ) { ArgumentNullException.ThrowIfNull( builder ); - var block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ); + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); + + var block = PipelineFactory.Start( inheritMiddleware ? parentMiddleware : null ); var function = builder( block ).CastFunction(); // cast because we don't know the final Pipe output value return new PipelineBuilder { - Function = new ForEachBlockBinder( Function ).Bind( function ), - Middleware = Middleware + Function = new ForEachBlockBinder( parentFunction ).Bind( function ), + Middleware = parentMiddleware }; } } + diff --git a/src/Hyperbee.Pipeline/Builders/HookBuilder.cs b/src/Hyperbee.Pipeline/Builders/HookBuilder.cs index e28d8a7..41ae3ee 100644 --- a/src/Hyperbee.Pipeline/Builders/HookBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/HookBuilder.cs @@ -1,37 +1,59 @@ using Hyperbee.Pipeline.Binders; +using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline; -public partial interface IPipelineStartBuilder +public static class HookBuilder { - IPipelineStartBuilder HookAsync( MiddlewareAsync functionMiddleware ); - IPipelineStartBuilder HookAsync( IEnumerable> functionMiddleware ); + public static IPipelineStartBuilder HookAsync( + this IPipelineStartBuilder parent, + MiddlewareAsync functionMiddleware + ) + { + return HookBuilder.HookAsync( parent, functionMiddleware ); + } + + public static IPipelineStartBuilder HookAsync( + this IPipelineStartBuilder parent, + IEnumerable> functionMiddleware + ) + { + return HookBuilder.HookAsync( parent, functionMiddleware ); + } } -public partial class PipelineBuilder +internal static class HookBuilder { - public IPipelineStartBuilder HookAsync( MiddlewareAsync functionMiddleware ) + public static IPipelineStartBuilder HookAsync( + IPipelineStartBuilder parent, + MiddlewareAsync functionMiddleware + ) { if ( functionMiddleware == null ) - return this; + return parent; + + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); return new PipelineBuilder { - Function = Function, - Middleware = new HookBinder( Middleware ).Bind( functionMiddleware ) + Function = parentFunction, + Middleware = new HookBinder( parentMiddleware ).Bind( functionMiddleware ) }; } - public IPipelineStartBuilder HookAsync( IEnumerable> functionMiddleware ) + public static IPipelineStartBuilder HookAsync( + IPipelineStartBuilder parent, + IEnumerable> functionMiddleware + ) { if ( functionMiddleware == null ) - return this; + return parent; - var builder = this as IPipelineStartBuilder; + var builder = parent; foreach ( var middleware in functionMiddleware ) { - builder = HookAsync( middleware ); + builder = HookAsync( parent, middleware ); } return builder; diff --git a/src/Hyperbee.Pipeline/Builders/PipeBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/PipeBlockBuilder.cs index f3f1153..aa9adbd 100644 --- a/src/Hyperbee.Pipeline/Builders/PipeBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/PipeBlockBuilder.cs @@ -1,57 +1,48 @@ using Hyperbee.Pipeline.Binders; +using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline; -public partial interface IPipelineBuilder +public static class PipeBlockBuilderExtensions { - IPipelineBuilder Pipe( Func, IPipelineBuilder> builder ); - IPipelineBuilder Pipe( bool inheritMiddleware, Func, IPipelineBuilder> builder ); - IPipelineBuilder PipeIf( Function condition, Func, IPipelineBuilder> builder ); - IPipelineBuilder PipeIf( Function condition, bool inheritMiddleware, Func, IPipelineBuilder> builder ); -} - -public partial class PipelineBuilder -{ - // Pipe the result of an inner builder to the next pipeline step. Acts like a Func. - - public IPipelineBuilder Pipe( Func, IPipelineBuilder> builder ) + public static IPipelineBuilder Pipe( + this IPipelineBuilder parent, + Func, IPipelineBuilder> builder + ) { - return Pipe( true, builder ); + return PipeBlockBuilder.Pipe( parent, true, builder ); } - public IPipelineBuilder Pipe( bool inheritMiddleware, Func, IPipelineBuilder> builder ) + public static IPipelineBuilder Pipe( + this IPipelineBuilder parent, + bool inheritMiddleware, + Func, IPipelineBuilder> builder + ) { - ArgumentNullException.ThrowIfNull( builder ); - - var block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ); - var function = ((PipelineBuilder) builder( block )).Function; - - return new PipelineBuilder - { - Function = new PipeBlockBinder( Function ).Bind( function ), - Middleware = Middleware - }; - } - - // PipeIf - - public IPipelineBuilder PipeIf( Function condition, Func, IPipelineBuilder> builder ) - { - return PipeIf( condition, true, builder ); + return PipeBlockBuilder.Pipe( parent, inheritMiddleware, builder ); } +} - public IPipelineBuilder PipeIf( Function condition, bool inheritMiddleware, Func, IPipelineBuilder> builder ) +internal static class PipeBlockBuilder +{ + public static IPipelineBuilder Pipe( + IPipelineBuilder parent, + bool inheritMiddleware, + Func, IPipelineBuilder> builder + ) { ArgumentNullException.ThrowIfNull( builder ); - ArgumentNullException.ThrowIfNull( condition ); - var block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ); + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); + + var block = PipelineFactory.Start( inheritMiddleware ? parentMiddleware : null ); var function = ((PipelineBuilder) builder( block )).Function; return new PipelineBuilder { - Function = new PipeBlockBinder( condition, Function ).Bind( function ), - Middleware = Middleware + Function = new PipeBlockBinder( parentFunction ).Bind( function ), + Middleware = parentMiddleware }; } } + diff --git a/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs new file mode 100644 index 0000000..596744e --- /dev/null +++ b/src/Hyperbee.Pipeline/Builders/PipeIfBlockBuilder.cs @@ -0,0 +1,51 @@ +using Hyperbee.Pipeline.Binders; +using Hyperbee.Pipeline.Extensions.Implementation; + +namespace Hyperbee.Pipeline; + +public static class PipeIfBlockBuilder +{ + public static IPipelineBuilder PipeIf( + this IPipelineBuilder parent, + Function condition, + Func, IPipelineBuilder> builder + ) + { + return PipeIfBlockBuilder.PipeIf( parent, condition, true, builder ); + } + + public static IPipelineBuilder PipeIf( + this IPipelineBuilder parent, + Function condition, + bool inheritMiddleware, + Func, IPipelineBuilder> builder + ) + { + return PipeIfBlockBuilder.PipeIf( parent, condition, inheritMiddleware, builder ); + } +} + +internal static class PipeIfBlockBuilder +{ + public static IPipelineBuilder PipeIf( + IPipelineBuilder parent, + Function condition, + bool inheritMiddleware, + Func, IPipelineBuilder> builder + ) + { + ArgumentNullException.ThrowIfNull( builder ); + ArgumentNullException.ThrowIfNull( condition ); + + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); + + var block = PipelineFactory.Start( inheritMiddleware ? parentMiddleware : null ); + var function = ((PipelineBuilder) builder( block )).Function; + + return new PipelineBuilder + { + Function = new PipeIfBlockBinder( condition, parentFunction ).Bind( function ), + Middleware = parentMiddleware + }; + } +} diff --git a/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs b/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs index 146f1da..28f4846 100644 --- a/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/PipeStatementBuilder.cs @@ -1,28 +1,64 @@ using Hyperbee.Pipeline.Binders; using Hyperbee.Pipeline.Context; +using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline; -public partial interface IPipelineBuilder +public static class PipeStatementBuilder { - IPipelineBuilder Pipe( Function next, string name ); - IPipelineBuilder Pipe( Function next, Action config = null ); - IPipelineBuilder PipeAsync( FunctionAsync next, string name ); - IPipelineBuilder PipeAsync( FunctionAsync next, Action config = null ); + public static IPipelineBuilder Pipe( + this IPipelineBuilder parent, + Function next, + string name + ) + { + return PipeStatementBuilder.Pipe( parent, next, config => config.Name = name ); + } + + public static IPipelineBuilder Pipe( + this IPipelineBuilder parent, + Function next, + Action config = null + ) + { + return PipeStatementBuilder.Pipe( parent, next, config ); + } + + public static IPipelineBuilder PipeAsync( + this IPipelineBuilder parent, + FunctionAsync next, + string name + ) + { + return PipeStatementBuilder.PipeAsync( parent, next, config => config.Name = name ); + } + + public static IPipelineBuilder PipeAsync( + this IPipelineBuilder parent, + FunctionAsync next, + Action config = null + ) + { + return PipeStatementBuilder.PipeAsync( parent, next, config ); + } } -public partial class PipelineBuilder +internal static class PipeStatementBuilder { - public IPipelineBuilder Pipe( Function next, string name ) => Pipe( next, config => config.Name = name ); - - public IPipelineBuilder Pipe( Function next, Action config = null ) + public static IPipelineBuilder Pipe( + IPipelineBuilder parent, + Function next, + Action config = null + ) { ArgumentNullException.ThrowIfNull( next ); + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); + return new PipelineBuilder { - Function = new PipeStatementBinder( Function, Middleware, config ).Bind( AsyncNext, next.Method ), - Middleware = Middleware + Function = new PipeStatementBinder( parentFunction, parentMiddleware, config ).Bind( AsyncNext, next.Method ), + Middleware = parentMiddleware }; // task wrapper @@ -33,16 +69,20 @@ Task AsyncNext( IPipelineContext context, TOutput argument ) } } - public IPipelineBuilder PipeAsync( FunctionAsync next, string name ) => PipeAsync( next, config => config.Name = name ); - - public IPipelineBuilder PipeAsync( FunctionAsync next, Action config = null ) + public static IPipelineBuilder PipeAsync( + IPipelineBuilder parent, + FunctionAsync next, + Action config = null + ) { ArgumentNullException.ThrowIfNull( next ); + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); + return new PipelineBuilder { - Function = new PipeStatementBinder( Function, Middleware, config ).Bind( next ), - Middleware = Middleware + Function = new PipeStatementBinder( parentFunction, parentMiddleware, config ).Bind( next ), + Middleware = parentMiddleware }; } } diff --git a/src/Hyperbee.Pipeline/Builders/ReduceBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/ReduceBlockBuilder.cs index 2910aad..da7dc2c 100644 --- a/src/Hyperbee.Pipeline/Builders/ReduceBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/ReduceBlockBuilder.cs @@ -1,32 +1,55 @@ using Hyperbee.Pipeline.Binders; +using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline; -public partial interface IPipelineBuilder +public static class ReduceBlockBuilder { - IPipelineBuilder Reduce( Func reducer, Func, IPipelineBuilder> builder ); - IPipelineBuilder ReduceAsync( bool inheritMiddleware, Func reducer, Func, IPipelineBuilder> builder ); -} + public static ReduceBlockBuilderWrapper Reduce( this IPipelineBuilder parent ) + { + return new ReduceBlockBuilderWrapper( parent ); + } -public partial class PipelineBuilder -{ - public IPipelineBuilder Reduce( Func reducer, Func, IPipelineBuilder> builder ) + public class ReduceBlockBuilderWrapper( IPipelineBuilder parent ) { - return ReduceAsync( true, reducer, builder ); + public IPipelineBuilder Type( + Func reducer, + Func, IPipelineBuilder> builder + ) + { + return ReduceBlockBuilder.ReduceAsync( parent, true, reducer, builder ); + } + + public IPipelineBuilder Type( + bool inheritMiddleware, + Func reducer, + Func, IPipelineBuilder> builder + ) + { + return ReduceBlockBuilder.ReduceAsync( parent, inheritMiddleware, reducer, builder ); + } } +} - public IPipelineBuilder ReduceAsync( bool inheritMiddleware, Func reducer, Func, IPipelineBuilder> builder ) +internal static class ReduceBlockBuilder +{ + public static IPipelineBuilder ReduceAsync( + IPipelineBuilder parent, + bool inheritMiddleware, + Func reducer, + Func, IPipelineBuilder> builder ) { ArgumentNullException.ThrowIfNull( builder ); - ArgumentNullException.ThrowIfNull( reducer ); - var block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ); + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); + + var block = PipelineFactory.Start( inheritMiddleware ? parentMiddleware : null ); var function = ((PipelineBuilder) builder( block )).Function; return new PipelineBuilder { - Function = new ReduceBlockBinder( reducer, Function ).Bind( function ), - Middleware = Middleware + Function = new ReduceBlockBinder( reducer, parentFunction ).Bind( function ), + Middleware = parentMiddleware }; } } diff --git a/src/Hyperbee.Pipeline/Builders/WaitAllBlockBuilder.cs b/src/Hyperbee.Pipeline/Builders/WaitAllBlockBuilder.cs index 3e8cb82..3e1327a 100644 --- a/src/Hyperbee.Pipeline/Builders/WaitAllBlockBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/WaitAllBlockBuilder.cs @@ -1,54 +1,85 @@ using Hyperbee.Pipeline.Binders; using Hyperbee.Pipeline.Context; +using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline; public delegate TOutput WaitAllReducer( IPipelineContext context, TInput input, WaitAllResult[] results ); -public partial interface IPipelineBuilder +public static class WaitAllBlockBuilder { - IPipelineBuilder WaitAll( Func, Func, IPipelineBuilder>[]> builders, WaitAllReducer reducer, Action config = null ); - IPipelineBuilder WaitAll( bool inheritMiddleware, Func, Func, IPipelineBuilder>[]> builders, WaitAllReducer reducer, Action config = null ); - IPipelineBuilder WaitAll( Func, Func, IPipelineBuilder>[]> builders, Action config = null ); - IPipelineBuilder WaitAll( bool inheritMiddleware, Func, Func, IPipelineBuilder>[]> builders, Action config = null ); + public static IPipelineBuilder WaitAll( + this IPipelineBuilder parent, + Func, Func, IPipelineBuilder>[]> builders, + WaitAllReducer reducer, + Action config = null ) + { + return WaitAllBlockBuilder.WaitAll( parent, true, builders, reducer, config ); + } + + public static IPipelineBuilder WaitAll( + this IPipelineBuilder parent, + bool inheritMiddleware, + Func, Func, IPipelineBuilder>[]> builders, + WaitAllReducer reducer, + Action config = null ) + { + return WaitAllBlockBuilder.WaitAll( parent, inheritMiddleware, builders, reducer, config ); + } + + public static IPipelineBuilder WaitAll( + this IPipelineBuilder parent, + Func, Func, IPipelineBuilder>[]> builders, + Action config = null ) + { + return WaitAllBlockBuilder.WaitAll( parent, true, builders, config ); + } + + public static IPipelineBuilder WaitAll( + this IPipelineBuilder parent, + bool inheritMiddleware, + Func, Func, IPipelineBuilder>[]> builders, + Action config = null ) + { + return WaitAllBlockBuilder.WaitAll( parent, inheritMiddleware, builders, config ); + } } -public partial class PipelineBuilder +internal static class WaitAllBlockBuilder { - public IPipelineBuilder WaitAll( - Func, - Func, IPipelineBuilder>[]> builders, + public static IPipelineBuilder WaitAll( + IPipelineBuilder parent, + Func, Func, IPipelineBuilder>[]> builders, WaitAllReducer reducer, Action config = null ) { - return WaitAll( true, builders, reducer, config ); + return WaitAll( parent, true, builders, reducer, config ); } - public IPipelineBuilder WaitAll( - Func, - Func, - IPipelineBuilder>[]> builders, + public static IPipelineBuilder WaitAll( + IPipelineBuilder parent, + Func, Func, IPipelineBuilder>[]> builders, Action config = null ) { - return WaitAll( true, builders, config ); + return WaitAll( parent, true, builders, config ); } - public IPipelineBuilder WaitAll( + public static IPipelineBuilder WaitAll( + IPipelineBuilder parent, bool inheritMiddleware, - Func, - Func, IPipelineBuilder>[]> builders, + Func, Func, IPipelineBuilder>[]> builders, Action config = null ) { - return WaitAll( true, builders, DefaultReducer, config ); + return WaitAll( parent, inheritMiddleware, builders, DefaultReducer, config ); // create a default reducer that returns the arg from the previous step static TOutput DefaultReducer( IPipelineContext ctx, TOutput arg, WaitAllResult[] results ) => arg; } - public IPipelineBuilder WaitAll( + public static IPipelineBuilder WaitAll( + IPipelineBuilder parent, bool inheritMiddleware, - Func, - Func, IPipelineBuilder>[]> builders, + Func, Func, IPipelineBuilder>[]> builders, WaitAllReducer reducer, Action config = null ) { @@ -59,15 +90,17 @@ public IPipelineBuilder WaitAll( if ( builderInstances.Length == 0 ) throw new ArgumentOutOfRangeException( nameof( builders ) ); + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); + var functions = builderInstances - .Select( builder => new { builder, block = PipelineFactory.Start( inheritMiddleware ? Middleware : null ) } ) + .Select( builder => new { builder, block = PipelineFactory.Start( inheritMiddleware ? parentMiddleware : null ) } ) .Select( x => x.builder( x.block ).CastFunction() ) .ToArray(); return new PipelineBuilder { - Function = new WaitAllBlockBinder( Function, Middleware, config ).Bind( functions, reducer ), - Middleware = Middleware + Function = new WaitAllBlockBinder( parentFunction, parentMiddleware, config ).Bind( functions, reducer ), + Middleware = parentMiddleware }; } } diff --git a/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs b/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs index 8e292e4..d3b91fe 100644 --- a/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs +++ b/src/Hyperbee.Pipeline/Builders/WrapBuilder.cs @@ -1,41 +1,73 @@ using Hyperbee.Pipeline.Binders; using Hyperbee.Pipeline.Context; +using Hyperbee.Pipeline.Extensions.Implementation; namespace Hyperbee.Pipeline; -public partial interface IPipelineBuilder +public static class WrapBuilder { - IPipelineBuilder WrapAsync( MiddlewareAsync pipelineMiddleware, string name ); - IPipelineBuilder WrapAsync( MiddlewareAsync pipelineMiddleware, Action config = null ); - IPipelineBuilder WrapAsync( IEnumerable> pipelineMiddleware, Action config = null ); + public static IPipelineBuilder WrapAsync( + this IPipelineBuilder parent, + MiddlewareAsync pipelineMiddleware, + string name + ) + { + return WrapBuilder.WrapAsync( parent, pipelineMiddleware, config => config.Name = name ); + } + + public static IPipelineBuilder WrapAsync( + this IPipelineBuilder parent, + MiddlewareAsync pipelineMiddleware, + Action config = null + ) + { + return WrapBuilder.WrapAsync( parent, pipelineMiddleware, config ); + } + + public static IPipelineBuilder WrapAsync( + this IPipelineBuilder parent, + IEnumerable> pipelineMiddleware, + Action config = null + ) + { + return WrapBuilder.WrapAsync( parent, pipelineMiddleware, config ); + } } -public partial class PipelineBuilder +internal static class WrapBuilder { - public IPipelineBuilder WrapAsync( MiddlewareAsync pipelineMiddleware, string name ) => WrapAsync( pipelineMiddleware, config => config.Name = name ); - - public IPipelineBuilder WrapAsync( MiddlewareAsync pipelineMiddleware, Action config = null ) + public static IPipelineBuilder WrapAsync( + IPipelineBuilder parent, + MiddlewareAsync pipelineMiddleware, + Action config = null + ) { if ( pipelineMiddleware == null ) - return this; + return parent; + + var (parentFunction, parentMiddleware) = parent.GetPipelineFunction(); return new PipelineBuilder { - Function = new WrapBinder( pipelineMiddleware, config ).Bind( Function ), - Middleware = Middleware + Function = new WrapBinder( pipelineMiddleware, config ).Bind( parentFunction ), + Middleware = parentMiddleware }; } - public IPipelineBuilder WrapAsync( IEnumerable> pipelineMiddleware, Action config = null ) + public static IPipelineBuilder WrapAsync( + IPipelineBuilder parent, + IEnumerable> pipelineMiddleware, + Action config = null + ) { if ( pipelineMiddleware == null ) - return this; + return parent; - var builder = this as IPipelineBuilder; + var builder = parent; foreach ( var middleware in pipelineMiddleware ) { - builder = WrapAsync( middleware, config ); + builder = WrapAsync( parent, middleware, config ); } return builder; diff --git a/src/Hyperbee.Pipeline/Context/IPipelineContext.cs b/src/Hyperbee.Pipeline/Context/IPipelineContext.cs index 531e5f8..67d1bbb 100644 --- a/src/Hyperbee.Pipeline/Context/IPipelineContext.cs +++ b/src/Hyperbee.Pipeline/Context/IPipelineContext.cs @@ -1,5 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; namespace Hyperbee.Pipeline.Context; diff --git a/src/Hyperbee.Pipeline/Hyperbee.Pipeline.csproj b/src/Hyperbee.Pipeline/Hyperbee.Pipeline.csproj index 8066f99..7177ab8 100644 --- a/src/Hyperbee.Pipeline/Hyperbee.Pipeline.csproj +++ b/src/Hyperbee.Pipeline/Hyperbee.Pipeline.csproj @@ -25,10 +25,7 @@ - - True - \ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Hyperbee.Pipeline/IPipelineBuilder.cs b/src/Hyperbee.Pipeline/IPipelineBuilder.cs index eb7120f..86b2c1c 100644 --- a/src/Hyperbee.Pipeline/IPipelineBuilder.cs +++ b/src/Hyperbee.Pipeline/IPipelineBuilder.cs @@ -33,12 +33,12 @@ public struct Empty; // // we solve for this using interfaces. -public partial interface IPipelineStartBuilder : IPipelineBuilder +public interface IPipelineStartBuilder : IPipelineBuilder { // head actions: (e.g. Hook) that are only valid at the start of the pipeline } -public partial interface IPipelineBuilder : IPipelineFinalBuilder +public interface IPipelineBuilder : IPipelineFinalBuilder { // normal actions } diff --git a/src/Hyperbee.Pipeline/IPipelineFunction.cs b/src/Hyperbee.Pipeline/IPipelineFunction.cs index be6f538..e4b6b04 100644 --- a/src/Hyperbee.Pipeline/IPipelineFunction.cs +++ b/src/Hyperbee.Pipeline/IPipelineFunction.cs @@ -1,14 +1,20 @@ namespace Hyperbee.Pipeline; -public interface IPipelineFunctionProvider +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; } + + void Deconstruct( out FunctionAsync function, out MiddlewareAsync middleware ) + { + function = Function; + middleware = Middleware; + } } diff --git a/src/Hyperbee.Pipeline/PipelineBuilder.cs b/src/Hyperbee.Pipeline/PipelineBuilder.cs index 1323464..2dfe12d 100644 --- a/src/Hyperbee.Pipeline/PipelineBuilder.cs +++ b/src/Hyperbee.Pipeline/PipelineBuilder.cs @@ -2,7 +2,7 @@ namespace Hyperbee.Pipeline; -public partial class PipelineBuilder : PipelineFactory, IPipelineStartBuilder, IPipelineFunctionProvider +public class PipelineBuilder : PipelineFactory, IPipelineStartBuilder, IPipelineFunctionProvider { internal FunctionAsync Function { get; init; } internal MiddlewareAsync Middleware { get; init; } @@ -67,7 +67,7 @@ FunctionAsync IPipelineBuilder.CastFunction() static TType Cast( object value ) => (TType) value; } - // custom builder and binders need access to Function and Middleware + // custom builders and binders need access to Function and Middleware IPipelineFunction IPipelineFunctionProvider.GetPipelineFunction() { return new PipelineFunction @@ -81,11 +81,5 @@ public record PipelineFunction : IPipelineFunction { public FunctionAsync Function { get; init; } public MiddlewareAsync Middleware { get; init; } - - public void Deconstruct( out FunctionAsync function, out MiddlewareAsync middleware ) - { - function = Function; - middleware = Middleware; - } } } diff --git a/src/Hyperbee.Pipeline/README.md b/src/Hyperbee.Pipeline/README.md deleted file mode 100644 index 9f4c09b..0000000 --- a/src/Hyperbee.Pipeline/README.md +++ /dev/null @@ -1,133 +0,0 @@ -# Hyperbee Pipeline - -`Hyperbee.Pipeline` allows you to construct asynchronous fluent pipelines in .NET. A pipeline, in this context, refers to a -sequence of data processing elements arranged in series, where the output of one element serves as the input for the subsequent -element. - -Hyperbee pipelines are composable, reusable, and easy to test. They are designed to be used in a variety of scenarios, such -as data processing, message handling, and workflow automation. - -Some key features are: - -* Middleware -* Hooks -* Wraps -* Conditional flows -* Loops -* Parallel processing -* Dependency injection -* Early returns and cancellation -* Child pipelines - -## Why Use Pipelines - -Pipelines provide a structured approach to managing complex processes, promoting [SOLID](https://en.wikipedia.org/wiki/SOLID) -principles, including Inversion of Control (IoC) and Separation of Concerns (SoC). They enable composability, making it easier -to build, test, and maintain your code. By extending the benefits of middleware and request-response pipelines throughout your -application, you achieve greater modularity, scalability, and flexibility. This is especially critical in domains such as -healthcare, compliance auditing, identity and roles, and high-security environments where clear boundaries and responsibilities -are essential. Hyperbee.Pipeline ensures that the advantages of pipelines and middleware are not abandoned at the controller -implementation, addressing a common gap in many frameworks. By using a functional approach, Hyperbee.Pipeline ensures that your -pipelines are not only robust and maintainable but also highly adaptable to changing requirements. - - -## Getting Started - -To get started with Hyperbee.Json, refer to the [documentation](https://stillpoint-software.github.io/hyperbee.pipeline) for -detailed instructions and examples. - -Install via NuGet: - -```bash -dotnet add package Hyperbee.Pipeline -``` - -## Building and Executing Pipelines - -Pipelines are built using `PipelineFactory`. Once built, a pipeline is just an async function that takes a `PipelineContext` and -an optional input value as parameters, and returns a result. - -```csharp - var command = PipelineFactory - .Start() - .Pipe( ( ctx, arg ) => $"hello {arg}" ) - .Build(); - - var result = await command( new PipelineContext(), "pipeline" ); - - Assert.AreEqual( "hello pipeline", result ); -``` - -## Dependency Injection - -Sometimes Pipelines and Pipeline middleware need access to specific container services. This can be -accomplished by registering services with the `PipelineContextFactory`. This can be done through -DI configuration, or manually through the `PipelineContextFactoryProvider` if you are not using DI. - -Pipelines manage dependencies with a specialized container. This allows the implementor to control -the services that are exposed through the pipeline. If you want to expose all application -services then you can call `AddPipeline` and pass `includeAllServices: true`. - -Register pipelines with DI and provide Pipeline dependencies using the application container. - -```csharp -services.AddPipeline( includeAllServices: true ); -``` - -Register Pipelines with DI and provide Pipeline dependencies using a specialized container. - -```csharp -services.AddPipeline( (factoryServices, rootProvider) => -{ - factoryServices.AddTransient() - factoryServices.ProxyService( rootProvider ); // pull from root container -} ); -``` - -## Pipeline of Pipelines - -The `PipelineFactory` library allows you to use pipelines together. Since pipelines are just functions, they can be used -as input to other pipelines. This allows you to create complex data processing flows by reusing and chaining together -multiple pipelines. - -Here's an example of how to use pipelines together: - -```csharp -var pipeline2 = PipelineFactory - .Start() - .Pipe( ( ctx, arg ) => $"{arg} again!" ) - .Build(); - -var pipeline1 = PipelineFactory - .Start() - .Pipe( ( ctx, arg ) => $"hello {arg}" ) - .PipeAsync( pipeline2 ) - .Build(); - -var result = await pipeline1( new PipelineContext(), "you" ); - -Assert.AreEqual( "hello you again!", result ); -``` - -## Conditional Flow and Advanced Features - -The `PipelineFactory` library provides a variety of builders that allow you to customize the behavior of your pipelines. -These methods provide powerful functionality for manipulating data as it passes through the pipeline. - -- Functions -- Procedures -- Conditional Flow -- Iterators -- Reduce -- Parallel execution - -## Credits - -Hyperbee.Pipeline is built upon the great work of several open-source projects. Special thanks to: - -- [Just The Docs](https://github.com/just-the-docs/just-the-docs) for the documentation theme. - -## Contributing - -We welcome contributions! Please see our [Contributing Guide](https://github.com/Stillpoint-Software/.github/blob/main/.github/CONTRIBUTING.md) -for more details. diff --git a/test/Hyperbee.Pipeline.Tests/PipelineEnumerationTests.cs b/test/Hyperbee.Pipeline.Tests/PipelineEnumerationTests.cs index d76adef..6eb7a07 100644 --- a/test/Hyperbee.Pipeline.Tests/PipelineEnumerationTests.cs +++ b/test/Hyperbee.Pipeline.Tests/PipelineEnumerationTests.cs @@ -44,7 +44,7 @@ public async Task Pipeline_should_enumerate() var command = PipelineFactory .Start() .Pipe( ( ctx, arg ) => arg.Split( ' ' ) ) - .ForEach( builder => builder + .ForEach().Type( builder => builder .Pipe( ( ctx, arg ) => arg + "!" ) .Pipe( ( ctx, arg ) => count += 10 ) ) @@ -62,7 +62,7 @@ public async Task Pipeline_should_reduce() var command = PipelineFactory .Start() .Pipe( ( ctx, arg ) => arg.Split( ' ' ) ) - .Reduce( ( a, v ) => a + v, builder => builder + .Reduce().Type( ( a, v ) => a + v, builder => builder .Pipe( ( ctx, arg ) => int.Parse( arg ) + 10 ) ) .Pipe( ( ctx, arg ) => arg + 5 ) From b466cf08c66428b1a34a1adb3decc5a3c7e29ea1 Mon Sep 17 00:00:00 2001 From: Matt Edwards Date: Fri, 9 Aug 2024 15:56:27 -0400 Subject: [PATCH 2/2] Update create-release.yml --- .github/workflows/create-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index d18edb4..638f74c 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Check For Valid Prerelease - if: ${{ env.ALLOW_PRERELEASE == 'true' && github.event.inputs.is_prerelease == 'false' || github.ref == 'refs/heads/main'}} + if: ${{ ( env.ALLOW_PRERELEASE == 'true' && github.event.inputs.is_prerelease == 'false' ) || ( github.ref == 'refs/heads/main' && github.event.inputs.is_prerelease == 'true' ) }} run: | echo "Prereleases should not be triggered on the main branch, please use development or hotfix" exit 1