Skip to content

Commit 86e8c76

Browse files
Added support for input-only pipelines
1 parent 8953c0a commit 86e8c76

File tree

10 files changed

+290
-0
lines changed

10 files changed

+290
-0
lines changed

FluentPipelines/IPipelineBuilder.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,19 @@ public interface IPipelineBuilder<TInput, TOutput>
3636
/// <param name="step">The next step of the pipeline, processing the data from the previous step.</param>
3737
/// <returns>A builder that allows you to continue building the rest of the pipeline.</returns>
3838
IPipelineBuilder<TInput, TNext> Then<TNext>(IPipelineStep<TOutput, TNext> step);
39+
40+
/// <summary>
41+
/// Defines the final step of the pipeline, returning no output data.
42+
/// </summary>
43+
/// <param name="action">A function acting as the next step of the pipeline, processing the data from the previous step and completing the pipeline.</param>
44+
/// <returns>A builder that allows you to build the pipeline.</returns>
45+
IInPipelineBuilder<TInput> Then(InPipelineStepDelegate<TOutput> action);
46+
47+
/// <summary>
48+
/// Defines the final step of the pipeline, returning no output data.
49+
/// </summary>
50+
/// <param name="step">The next step of the pipeline, processing the data from the previous step and completing the pipeline.</param>
51+
/// <returns>A builder that allows you to build the pipeline.</returns>
52+
IInPipelineBuilder<TInput> Then(IInPipelineStep<TOutput> step);
3953
}
4054
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
3+
namespace FluentPipelines
4+
{
5+
/// <summary>
6+
/// Defines methods for a pipeline that takes <typeparamref name="TInput"/> as input, and has no output.
7+
/// </summary>
8+
/// <typeparam name="TInput">The type of data used as input to the pipeline.</typeparam>
9+
public interface IInPipeline<TInput>
10+
{
11+
/// <summary>
12+
/// Runs the pipeline.
13+
/// </summary>
14+
/// <param name="input">The input data.</param>
15+
void Run(TInput input);
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
3+
namespace FluentPipelines
4+
{
5+
/// <summary>
6+
/// Defines methods for a builder that constructs pipelines with input data and no output data.
7+
/// </summary>
8+
/// <typeparam name="TInput">The type of data used as input to the pipeline.</typeparam>
9+
public interface IInPipelineBuilder<TInput>
10+
{
11+
/// <summary>
12+
/// Compiles all steps and builds a runnable pipeline.
13+
/// </summary>
14+
/// <returns>The resulting pipeline.</returns>
15+
IInPipeline<TInput> Build();
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
3+
namespace FluentPipelines
4+
{
5+
/// <summary>
6+
/// Defines methods for a step in a pipeline with input data and no output data.
7+
/// </summary>
8+
/// <typeparam name="TInput">The type of data used as input to the step.</typeparam>
9+
public interface IInPipelineStep<TInput>
10+
{
11+
/// <summary>
12+
/// Executes the step.
13+
/// </summary>
14+
/// <param name="input">The input data to process.</param>
15+
void Run(TInput input);
16+
}
17+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
3+
namespace FluentPipelines
4+
{
5+
/// <summary>
6+
/// A pipeline step that executes a delegate method.
7+
/// </summary>
8+
/// <typeparam name="TInput">The type of data used as input to the step.</typeparam>
9+
public class InFunctionStep<TInput> : IInPipelineStep<TInput>
10+
{
11+
private readonly InPipelineStepDelegate<TInput> function;
12+
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="InFunctionStep{TInput}"/> class.
15+
/// </summary>
16+
/// <param name="function">The delegate method to execute.</param>
17+
public InFunctionStep(InPipelineStepDelegate<TInput> function)
18+
{
19+
this.function = function ?? throw new ArgumentNullException(nameof(function));
20+
}
21+
22+
/// <inheritdoc/>
23+
public void Run(TInput input)
24+
{
25+
function(input);
26+
}
27+
}
28+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace FluentPipelines
6+
{
7+
/// <summary>
8+
/// A pipeline that takes <typeparamref name="TInput"/> as input, and has no output.
9+
/// </summary>
10+
/// <typeparam name="TInput">The type of data used as input to the pipeline.</typeparam>
11+
public sealed class InPipeline<TInput> : IInPipeline<TInput>
12+
{
13+
private readonly PipelineStep[] steps;
14+
15+
/// <summary>
16+
/// Initializes a new instance of the <see cref="InPipeline{TInput}"/> class.
17+
/// </summary>
18+
/// <param name="steps">
19+
/// The steps to be executed as part of the pipeline.
20+
/// It is important that the first step takes <typeparamref name="TInput"/> as input.
21+
/// </param>
22+
public InPipeline(IEnumerable<PipelineStep> steps)
23+
{
24+
if(steps is null)
25+
throw new ArgumentNullException(nameof(steps));
26+
27+
if(!steps.Any())
28+
throw new ArgumentException("No steps specified", nameof(steps));
29+
30+
this.steps = steps.ToArray();
31+
}
32+
33+
/// <inheritdoc/>
34+
public void Run(TInput input)
35+
{
36+
steps.Execute(input);
37+
}
38+
}
39+
40+
/// <summary>
41+
/// A static class containing helper methods for <see cref="InPipeline{TInput}"/>.
42+
/// </summary>
43+
public static class InputPipeline
44+
{
45+
/// <summary>
46+
/// Creates an <see cref="InPipeline{TInput}"/> from a <see cref="InPipelineBuilder{TInput}"/>.
47+
/// </summary>
48+
/// <typeparam name="TInput">The type of data used as input to the pipeline.</typeparam>
49+
/// <param name="builder">The builder containing the steps to be executed as part of the pipeline.</param>
50+
/// <returns>The resulting pipeline.</returns>
51+
public static InPipeline<TInput> CreateFrom<TInput>(InPipelineBuilder<TInput> builder)
52+
{
53+
return new InPipeline<TInput>(builder.Steps);
54+
}
55+
}
56+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace FluentPipelines
5+
{
6+
/// <summary>
7+
/// A builder that builds a pipeline with input data and no output data.
8+
/// </summary>
9+
/// <typeparam name="TInput">The type of data used as input to the pipeline.</typeparam>
10+
public class InPipelineBuilder<TInput> : IInPipelineBuilder<TInput>
11+
{
12+
/// <summary>
13+
/// Gets the collection of steps added to the builder, which will be used to construct the pipeline.
14+
/// <para><b>IMPORTANT:</b> The first step must take <typeparamref name="TInput"/> as input, otherwise the built-in pipeline types will not work.</para>
15+
/// </summary>
16+
protected internal List<PipelineStep> Steps { get; }
17+
18+
/// <summary>
19+
/// Initializes a new instance of the <see cref="InPipelineBuilder{TInput}"/> class.
20+
/// </summary>
21+
/// <param name="steps">
22+
/// The collection of steps to add to the builder, which will be used to construct the pipeline.
23+
/// <para><b>IMPORTANT:</b> The first step must take <typeparamref name="TInput"/> as input, otherwise the built-in pipeline types will not work.</para>
24+
/// <para><b>IMPORTANT:</b> <paramref name="steps"/> is taken by reference! The list is NOT copied!</para>
25+
/// </param>
26+
protected internal InPipelineBuilder(List<PipelineStep> steps)
27+
{
28+
Steps = steps ?? throw new ArgumentNullException(nameof(steps));
29+
}
30+
31+
/// <summary>
32+
/// Initializes a new instance of the <see cref="InPipelineBuilder{TInput}"/> class.
33+
/// </summary>
34+
/// <param name="firstStep">
35+
/// The first step of the pipeline, processing the input data.
36+
/// <para><b>IMPORTANT:</b> This must take <typeparamref name="TInput"/> as input, otherwise the built-in pipeline types will not work.</para>
37+
/// </param>
38+
public InPipelineBuilder(PipelineStep firstStep)
39+
{
40+
if(firstStep is null)
41+
throw new ArgumentNullException(nameof(firstStep));
42+
43+
Steps = new List<PipelineStep>() { firstStep };
44+
}
45+
46+
/// <inheritdoc/>
47+
public virtual IInPipeline<TInput> Build()
48+
{
49+
return new InPipeline<TInput>(Steps);
50+
}
51+
}
52+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System;
2+
3+
namespace FluentPipelines
4+
{
5+
/// <summary>
6+
/// A pipeline step with input data and no output data.
7+
/// </summary>
8+
/// <typeparam name="TInput">The type of data used as input to the step.</typeparam>
9+
public sealed class InPipelineStep<TInput> : InPipelineStep
10+
{
11+
private readonly IInPipelineStep<TInput> step;
12+
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="InPipelineStep{TInput}"/> class.
15+
/// </summary>
16+
/// <param name="step">The base step to create this generic step from.</param>
17+
public InPipelineStep(IInPipelineStep<TInput> step)
18+
{
19+
this.step = step ?? throw new ArgumentNullException(nameof(step));
20+
}
21+
22+
internal override void Run(object input)
23+
{
24+
step.Run((TInput)input);
25+
}
26+
}
27+
28+
/// <summary>
29+
/// The base class for generic, typeless pipeline steps. Also contains helper methods to create strongly typed generic steps.
30+
/// </summary>
31+
public class InPipelineStep
32+
{
33+
/// <summary>
34+
/// Creates a <see cref="InPipelineStep{TInput}"/>.
35+
/// </summary>
36+
/// <typeparam name="TInput">The type of data used as input to the step.</typeparam>
37+
/// <param name="step">The base step to create the generic step from.</param>
38+
/// <returns>A generic pipeline step.</returns>
39+
public static InPipelineStep<TInput> Create<TInput>(IInPipelineStep<TInput> step)
40+
{
41+
return new InPipelineStep<TInput>(step);
42+
}
43+
44+
internal InPipelineStep()
45+
{
46+
}
47+
48+
internal virtual void Run(object input)
49+
{
50+
throw new NotImplementedException();
51+
}
52+
}
53+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace FluentPipelines
4+
{
5+
/// <summary>
6+
/// A delegate method representing a pipeline step.
7+
/// </summary>
8+
/// <typeparam name="TInput">The type of data used as input to the step.</typeparam>
9+
/// <param name="input">The input data to process.</param>
10+
public delegate void InPipelineStepDelegate<in TInput>(TInput input);
11+
}

FluentPipelines/PipelineBuilder.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,30 @@ public virtual IPipelineBuilder<TInput, TNext> Then<TNext>(IPipelineStep<TOutput
6868

6969
return new PipelineBuilder<TInput, TNext>(Steps);
7070
}
71+
72+
/// <inheritdoc/>
73+
public IInPipelineBuilder<TInput> Then(InPipelineStepDelegate<TOutput> action)
74+
{
75+
if(action is null)
76+
throw new ArgumentNullException(nameof(action));
77+
78+
return Then(new InFunctionStep<TOutput>(action));
79+
}
80+
81+
/// <inheritdoc/>
82+
public IInPipelineBuilder<TInput> Then(IInPipelineStep<TOutput> step)
83+
{
84+
if(step is null)
85+
throw new ArgumentNullException(nameof(step));
86+
87+
Steps.Add(new PipelineStep<TOutput, object>(
88+
new FunctionStep<TOutput, object>(input => {
89+
step.Run(input);
90+
return null;
91+
})
92+
));
93+
94+
return new InPipelineBuilder<TInput>(Steps);
95+
}
7196
}
7297
}

0 commit comments

Comments
 (0)