Skip to content

Commit 5c970a9

Browse files
authored
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 <[email protected]> * [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 <[email protected]> * [FEATURE]: Improve builder pattern (#35) * Refactor builders to eliminate reliance on partial interfaces and classes --------- Co-authored-by: Brenton Farmer <[email protected]> * Fix footer * Previous version was 'v1.1.6'. Version now 'v1.2.0'. --------- Co-authored-by: Brenton Farmer <[email protected]>
1 parent b11d002 commit 5c970a9

37 files changed

+766
-506
lines changed

Directory.Build.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
<!-- Solution version numbers -->
33
<PropertyGroup>
44
<MajorVersion>1</MajorVersion>
5-
<MinorVersion>1</MinorVersion>
6-
<PatchVersion>6</PatchVersion>
5+
<MinorVersion>2</MinorVersion>
6+
<PatchVersion>0</PatchVersion>
77
</PropertyGroup>
88
<!-- Disable automatic package publishing -->
99
<PropertyGroup>

Hyperbee.Pipeline.sln

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
77
ProjectSection(SolutionItems) = preProject
88
Directory.Build.props = Directory.Build.props
99
Directory.Build.targets = Directory.Build.targets
10-
LICENSE = LICENSE
11-
README.md = README.md
1210
solution-helper.psm1 = solution-helper.psm1
1311
EndProjectSection
1412
EndProject

docs/_includes/nav_footer_custom.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
<footer class="site-footer">
2-
Hyperbee Json Docs
2+
Hyperbee Pipeline Docs
33
</footer>

docs/syntax.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ var count = 0;
101101
var command = PipelineFactory
102102
.Start<string>()
103103
.Pipe( ( ctx, arg ) => arg.Split( ' ' ) )
104-
.ForEach<string>( builder => builder
104+
.ForEach().Type<string>( builder => builder
105105
.Pipe( ( ctx, arg ) => count += 10 )
106106
)
107107
.Pipe( ( ctx, arg ) => count += 5 )
@@ -117,6 +117,21 @@ Assert.AreEqual( count, 25 );
117117
`Reduce` and `ReduceAync` allow you to transform an enumerable pipeline input to a single value. You can specify a reducer function
118118
that defines how the elements should be combined, and a builder function that creates the pipeline for processing the elements.### Cancel
119119

120+
```csharp
121+
var command = PipelineFactory
122+
.Start<string>()
123+
.Pipe( ( ctx, arg ) => arg.Split( ' ' ) )
124+
.Reduce().Type<string, int>( ( aggregate, value ) => aggregate + value, builder => builder
125+
.Pipe( ( ctx, arg ) => int.Parse( arg ) + 10 )
126+
)
127+
.Pipe( ( ctx, arg ) => arg + 5 )
128+
.Build();
129+
130+
var result = await command( new PipelineContext(), "1 2 3 4 5" );
131+
132+
Assert.AreEqual( result, 70 );
133+
```
134+
120135
### WaitAll
121136

122137
`WaitAll` allows you to wait for concurrent pipelines to complete before continuing. You can specify a set of builders that create
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Hyperbee.Pipeline.Context;
2+
using Hyperbee.Pipeline.Extensions.Implementation;
3+
4+
namespace Hyperbee.Pipeline.Binders.Abstractions;
5+
6+
internal abstract class Binder<TInput, TOutput>
7+
{
8+
protected FunctionAsync<TInput, TOutput> Pipeline { get; }
9+
protected Action<IPipelineContext> Configure { get; }
10+
11+
protected Binder( FunctionAsync<TInput, TOutput> function, Action<IPipelineContext> configure )
12+
{
13+
Pipeline = function;
14+
Configure = configure;
15+
}
16+
17+
protected virtual async Task<(TOutput Result, bool Canceled)> ProcessPipelineAsync( IPipelineContext context, TInput argument )
18+
{
19+
var result = await Pipeline( context, argument ).ConfigureAwait( false );
20+
21+
var contextControl = (IPipelineContextControl) context;
22+
var canceled = contextControl.HandleCancellationRequested( result );
23+
24+
return (canceled ? default : result, canceled);
25+
}
26+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Hyperbee.Pipeline.Context;
2+
3+
namespace Hyperbee.Pipeline.Binders.Abstractions;
4+
5+
internal abstract class BlockBinder<TInput, TOutput> : Binder<TInput, TOutput>
6+
{
7+
protected BlockBinder( FunctionAsync<TInput, TOutput> function, Action<IPipelineContext> configure )
8+
: base( function, configure )
9+
{
10+
}
11+
12+
// Using TArgument instead of TOutput allows more capabilities for special
13+
// use cases where the next argument is not the same as the output type
14+
// like ReduceBlockBinder and ForEachBlockBinder
15+
16+
protected virtual async Task<TNext> ProcessBlockAsync<TArgument, TNext>( FunctionAsync<TArgument, TNext> blockFunction, IPipelineContext context, TArgument nextArgument )
17+
{
18+
return await blockFunction( context, nextArgument ).ConfigureAwait( false );
19+
}
20+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.Runtime.CompilerServices;
2+
using Hyperbee.Pipeline.Context;
3+
4+
namespace Hyperbee.Pipeline.Binders.Abstractions;
5+
6+
internal abstract class ConditionalBlockBinder<TInput, TOutput> : BlockBinder<TInput, TOutput>
7+
{
8+
protected Function<TOutput, bool> Condition { get; }
9+
10+
protected ConditionalBlockBinder( Function<TOutput, bool> condition, FunctionAsync<TInput, TOutput> function, Action<IPipelineContext> configure )
11+
: base( function, configure )
12+
{
13+
Condition = condition;
14+
}
15+
16+
protected override async Task<TNext> ProcessBlockAsync<TArgument, TNext>( FunctionAsync<TArgument, TNext> blockFunction, IPipelineContext context, TArgument nextArgument )
17+
{
18+
if ( Condition != null && !Condition( context, CastTypeArg<TArgument, TOutput>( nextArgument ) ) )
19+
{
20+
return CastTypeArg<TArgument, TNext>( nextArgument );
21+
}
22+
23+
return await base.ProcessBlockAsync( blockFunction, context, nextArgument ).ConfigureAwait( false );
24+
}
25+
26+
[MethodImpl( MethodImplOptions.AggressiveInlining )]
27+
private static TResult CastTypeArg<TType, TResult>( TType input )
28+
{
29+
return (TResult) (object) input;
30+
}
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Hyperbee.Pipeline.Context;
2+
using Hyperbee.Pipeline.Extensions.Implementation;
3+
4+
namespace Hyperbee.Pipeline.Binders.Abstractions;
5+
6+
internal abstract class StatementBinder<TInput, TOutput> : Binder<TInput, TOutput>
7+
{
8+
protected MiddlewareAsync<object, object> Middleware { get; }
9+
10+
protected StatementBinder( FunctionAsync<TInput, TOutput> function, MiddlewareAsync<object, object> middleware, Action<IPipelineContext> configure )
11+
: base( function, configure )
12+
{
13+
Middleware = middleware;
14+
}
15+
16+
protected virtual async Task<TNext> ProcessStatementAsync<TNext>( FunctionAsync<TOutput, TNext> nextFunction, IPipelineContext context, TOutput nextArgument, string frameName )
17+
{
18+
var contextControl = (IPipelineContextControl) context;
19+
20+
using var _ = contextControl.CreateFrame( context, Configure, frameName );
21+
22+
if ( Middleware == null )
23+
return await nextFunction( context, nextArgument ).ConfigureAwait( false );
24+
25+
return (TNext) await Middleware(
26+
context,
27+
nextArgument,
28+
async ( context1, argument1 ) => await nextFunction( context1, (TOutput) argument1 ).ConfigureAwait( false )
29+
).ConfigureAwait( false );
30+
}
31+
}

src/Hyperbee.Pipeline/Binders/CallBlockBinder.cs

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,24 @@
1-
namespace Hyperbee.Pipeline.Binders;
1+
using Hyperbee.Pipeline.Binders.Abstractions;
22

3-
internal class CallBlockBinder<TInput, TOutput>
4-
{
5-
private FunctionAsync<TInput, TOutput> Pipeline { get; }
6-
private Function<TOutput, bool> Condition { get; }
3+
namespace Hyperbee.Pipeline.Binders;
74

5+
internal class CallBlockBinder<TInput, TOutput> : BlockBinder<TInput, TOutput>
6+
{
87
public CallBlockBinder( FunctionAsync<TInput, TOutput> function )
9-
: this( null, function )
10-
{
11-
}
12-
13-
public CallBlockBinder( Function<TOutput, bool> condition, FunctionAsync<TInput, TOutput> function )
8+
: base( function, default )
149
{
15-
Condition = condition;
16-
Pipeline = function;
1710
}
1811

1912
public FunctionAsync<TInput, TOutput> Bind( FunctionAsync<TOutput, object> next )
2013
{
2114
return async ( context, argument ) =>
2215
{
23-
var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false );
16+
var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false );
2417

25-
if ( Condition == null || Condition( context, nextArgument ) )
26-
await next( context, nextArgument ).ConfigureAwait( false );
18+
if ( canceled )
19+
return default;
2720

21+
await ProcessBlockAsync( next, context, nextArgument ).ConfigureAwait( false );
2822
return nextArgument;
2923
};
3024
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Hyperbee.Pipeline.Binders.Abstractions;
2+
3+
namespace Hyperbee.Pipeline.Binders;
4+
5+
internal class CallIfBlockBinder<TInput, TOutput> : ConditionalBlockBinder<TInput, TOutput>
6+
{
7+
public CallIfBlockBinder( Function<TOutput, bool> condition, FunctionAsync<TInput, TOutput> function )
8+
: base( condition, function, default )
9+
{
10+
}
11+
12+
public FunctionAsync<TInput, TOutput> Bind( FunctionAsync<TOutput, object> next )
13+
{
14+
return async ( context, argument ) =>
15+
{
16+
var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false );
17+
18+
if ( canceled )
19+
return default;
20+
21+
await ProcessBlockAsync( next, context, nextArgument ).ConfigureAwait( false );
22+
return nextArgument;
23+
};
24+
}
25+
}
Lines changed: 12 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
11
using System.Reflection;
2+
using Hyperbee.Pipeline.Binders.Abstractions;
23
using Hyperbee.Pipeline.Context;
3-
using Hyperbee.Pipeline.Extensions.Implementation;
44

55
namespace Hyperbee.Pipeline.Binders;
66

7-
internal class CallStatementBinder<TInput, TOutput>
7+
internal class CallStatementBinder<TInput, TOutput> : StatementBinder<TInput, TOutput>
88
{
9-
private FunctionAsync<TInput, TOutput> Pipeline { get; }
10-
private MiddlewareAsync<object, object> Middleware { get; }
11-
private Action<IPipelineContext> Configure { get; }
12-
139
public CallStatementBinder( FunctionAsync<TInput, TOutput> function, MiddlewareAsync<object, object> middleware, Action<IPipelineContext> configure )
10+
: base( function, middleware, configure )
1411
{
15-
Pipeline = function;
16-
Middleware = middleware;
17-
Configure = configure;
1812
}
1913

2014
public FunctionAsync<TInput, TOutput> Bind( ProcedureAsync<TOutput> next, MethodInfo method = null )
@@ -23,38 +17,18 @@ public FunctionAsync<TInput, TOutput> Bind( ProcedureAsync<TOutput> next, Method
2317

2418
return async ( context, argument ) =>
2519
{
26-
var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false );
27-
28-
var contextControl = (IPipelineContextControl) context;
20+
var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false );
2921

30-
if ( contextControl.HandleCancellationRequested( nextArgument ) )
22+
if ( canceled )
3123
return default;
3224

33-
using ( contextControl.CreateFrame( context, Configure, defaultName ) )
34-
{
35-
return await Next( next, context, nextArgument ).ConfigureAwait( false );
36-
}
25+
return await ProcessStatementAsync(
26+
async ( ctx, arg ) =>
27+
{
28+
await next( ctx, arg ).ConfigureAwait( false );
29+
return arg;
30+
}, context, nextArgument, defaultName ).ConfigureAwait( false );
3731
};
3832
}
39-
40-
private async Task<TOutput> Next( ProcedureAsync<TOutput> next, IPipelineContext context, TOutput nextArgument )
41-
{
42-
if ( Middleware == null )
43-
{
44-
await next( context, nextArgument ).ConfigureAwait( false );
45-
return nextArgument;
46-
}
47-
48-
await Middleware(
49-
context,
50-
nextArgument,
51-
async ( context1, argument1 ) =>
52-
{
53-
await next( context1, (TOutput) argument1 ).ConfigureAwait( false );
54-
return nextArgument;
55-
}
56-
).ConfigureAwait( false );
57-
58-
return nextArgument;
59-
}
6033
}
34+
Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,33 @@
1-
namespace Hyperbee.Pipeline.Binders;
1+
using Hyperbee.Pipeline.Binders.Abstractions;
22

3-
internal class ForEachBlockBinder<TInput, TOutput, TElement>
4-
{
5-
private FunctionAsync<TInput, TOutput> Pipeline { get; }
3+
namespace Hyperbee.Pipeline.Binders;
64

5+
internal class ForEachBlockBinder<TInput, TOutput, TElement> : BlockBinder<TInput, TOutput>
6+
{
77
public ForEachBlockBinder( FunctionAsync<TInput, TOutput> function )
8+
: base( function, default )
89
{
9-
Pipeline = function;
1010
}
1111

1212
public FunctionAsync<TInput, TOutput> Bind( FunctionAsync<TElement, object> next )
13+
1314
{
1415
return async ( context, argument ) =>
1516
{
16-
var nextArgument = await Pipeline( context, argument ).ConfigureAwait( false );
17+
var (nextArgument, canceled) = await ProcessPipelineAsync( context, argument ).ConfigureAwait( false );
18+
19+
if ( canceled )
20+
return default;
21+
1722
var nextArguments = (IEnumerable<TElement>) nextArgument;
1823

1924
foreach ( var elementArgument in nextArguments )
2025
{
21-
await next( context, elementArgument ).ConfigureAwait( false );
26+
await ProcessBlockAsync( next, context, elementArgument ).ConfigureAwait( false );
2227
}
2328

2429
return nextArgument;
2530
};
2631
}
2732
}
33+

src/Hyperbee.Pipeline/Binders/HookBinder.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
namespace Hyperbee.Pipeline.Binders;
22

3-
internal class HookBinder<TInput1, TOutput1> // explicit Type Args due to <object,object> usage
3+
internal class HookBinder<TInput, TOutput> // explicit Type Args due to <object,object> usage
44
{
5-
private MiddlewareAsync<TInput1, TOutput1> Middleware { get; }
5+
private MiddlewareAsync<TInput, TOutput> Middleware { get; }
66

7-
public HookBinder( MiddlewareAsync<TInput1, TOutput1> middleware )
7+
public HookBinder( MiddlewareAsync<TInput, TOutput> middleware )
88
{
99
Middleware = middleware ?? (async ( context, argument, next ) => await next( context, argument ).ConfigureAwait( false ));
1010
}
1111

12-
public MiddlewareAsync<TInput1, TOutput1> Bind( MiddlewareAsync<TInput1, TOutput1> middleware )
12+
public MiddlewareAsync<TInput, TOutput> Bind( MiddlewareAsync<TInput, TOutput> middleware )
1313
{
1414
return async ( context, argument, function ) =>
1515
await middleware(

0 commit comments

Comments
 (0)