Skip to content

Commit

Permalink
remove nested calls to child blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
danielklecha committed May 17, 2024
1 parent f5c576f commit 4c1f6d9
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 49 deletions.
5 changes: 3 additions & 2 deletions PipelineBlocks/Models/BlockResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ internal BlockResult(object? data, string? key, string? message, BlockResultType
ResultType = resultType;
}

public static BlockResult Completed() => new(default, null, null, BlockResultType.Completed);
public static BlockResult Completed(string? message = null) => new(default, null, message, BlockResultType.Completed);
public static BlockResult Error(string? message = null) => new(default, null, message, BlockResultType.Error);
public static BlockResult<T> Exit<T>(T? data = default) => new(data, null, null, BlockResultType.Exit);
public static BlockResult<T> Forward<T>(T? data = default) => new(data, null, null, BlockResultType.Forward);
public static BlockResult<T> Execute<T>(T? data = default) => new(data, null, null, BlockResultType.Execute);
}

public class BlockResult<T> : BlockResult
Expand All @@ -34,7 +35,7 @@ internal BlockResult(T? data, string? key, string? message, BlockResultType resu
Data = data;
}

public new static BlockResult<T> Completed() => new(default, null, null, BlockResultType.Completed);
public new static BlockResult<T> Completed(string? message = null) => new(default, null, message, BlockResultType.Completed);
public static BlockResult<T> Skip() => new(default, null, null, BlockResultType.Skip);
public static new BlockResult<T> Error(string? message = null) => new(default, null, message, BlockResultType.Error);
public static BlockResult<T> BackToCheckpoint(string? key = null) => new(default, key, null, BlockResultType.BackToCheckpoint);
Expand Down
3 changes: 2 additions & 1 deletion PipelineBlocks/Models/BlockResultType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ public enum BlockResultType
Forward,
BackToCheckpoint,
BackToExit,
Exit
Exit,
Execute
}
9 changes: 8 additions & 1 deletion PipelineBlocks/Models/IExecutableBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@
public interface IExecutableBlock
{
/// <summary>
/// Process the block
/// Process the block and all desendants
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<BlockResult> ExecuteAsync(CancellationToken cancellationToken = default);

/// <summary>
/// Process the block without descendants
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<BlockResult> ExecuteSelfAsync(CancellationToken cancellationToken = default);
}
68 changes: 46 additions & 22 deletions PipelineBlocks/Models/PipelineBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class PipelineBlock<T> : IPipelineBlock<T>

public IBlock? Child => ChildCondition?.Invoke(this);

public async Task<BlockResult> ExecuteAsync(CancellationToken cancellationToken = default)
async Task<BlockResult> IExecutableBlock.ExecuteSelfAsync(CancellationToken cancellationToken)
{
if (!this.IsActive())
return BlockResult.Error("Block is not active");
Expand All @@ -34,64 +34,86 @@ public async Task<BlockResult> ExecuteAsync(CancellationToken cancellationToken
return BlockResult.Error("No job");
}
var result = await Job.Invoke(this, cancellationToken);
if (result is null)
return BlockResult.Error("Job returned null");
return result.ResultType switch
{
BlockResultType.Exit => await ExitAsync(result, cancellationToken),
BlockResultType.Forward => await ForwardAsync(result, cancellationToken),
BlockResultType.BackToCheckpoint => await BackToCheckpointAsync(result, cancellationToken),
BlockResultType.BackToExit => await BackToExitAsync(result, cancellationToken),
BlockResultType.Skip => await SkipAsync(cancellationToken),
BlockResultType.Exit or BlockResultType.Completed => Exit(result),
BlockResultType.Forward => Forward(result),
BlockResultType.BackToCheckpoint => BackToCheckpoint(result),
BlockResultType.BackToExit => BackToExit(result),
BlockResultType.Skip => Skip(),
_ => result
};
}

private Task<BlockResult> ExitAsync(BlockResult<T> result, CancellationToken cancellationToken)
public async Task<BlockResult> ExecuteAsync(CancellationToken cancellationToken = default)
{
var result = await (this as IExecutableBlock).ExecuteSelfAsync(cancellationToken);
while (true)
{
if (result is null)
return BlockResult.Error("Block returned null");
switch (result.ResultType)
{
case BlockResultType.Execute when result.Data is IExecutableBlock block:
if (block == this)
return BlockResult.Error("Child is current block");
result = await block.ExecuteSelfAsync();
break;
default:
return result;
}
}
}

private BlockResult Exit(BlockResult<T> result)
{
if (!HasExit)
return Task.FromResult(BlockResult.Error("No Exit"));
return BlockResult.Error("No Exit");
Data = result.Data;
IsCompleted = true;
return Task.FromResult(BlockResult.Completed());
return BlockResult.Completed("Exit result");
}

public bool IsCompleted { get; private set; }

object? IBlock.Data => Data;

private async Task<BlockResult> BackToCheckpointAsync(BlockResult result, CancellationToken cancellationToken)
private BlockResult BackToCheckpoint(BlockResult result)
{
var targetDescendant = this.EnumerateAncestors().OfType<IParentBlock>().FirstOrDefault(x => x.IsCheckpoint && (result.Key == null || string.Equals(result.Key, x.Key, StringComparison.OrdinalIgnoreCase)));
if (targetDescendant == null)
return BlockResult.Error("Unable to find checkpoint");
(this as IParentBlock).Reset();
foreach (var descendant in this.EnumerateAncestors().OfType<IParentBlock>().TakeWhile(x => x != targetDescendant))
descendant.Reset();
return await targetDescendant.ExecuteAsync(cancellationToken);
return BlockResult.Execute<IExecutableBlock>(targetDescendant);
}

private Task<BlockResult> BackToExitAsync(BlockResult result, CancellationToken cancellationToken)
private BlockResult BackToExit(BlockResult result)
{
var targetAncestor = this.EnumerateAncestors().OfType<IParentBlock>().FirstOrDefault(x => x.HasExit && (result.Key == null || string.Equals(result.Key, x.Key, StringComparison.OrdinalIgnoreCase)));
if (targetAncestor == null)
return Task.FromResult(BlockResult.Error("Unable to find exit"));
return BlockResult.Error("Unable to find exit");
(this as IParentBlock).Reset();
foreach (var descendant in this.EnumerateAncestors().OfType<IParentBlock>().TakeWhile(x => x != targetAncestor))
descendant.Reset();
return Task.FromResult(BlockResult.Completed());
return BlockResult.Completed("Back to exit result");
}

private async Task<BlockResult> ForwardAsync(BlockResult<T> result, CancellationToken cancellationToken)
private BlockResult Forward(BlockResult<T> result)
{
Data = result.Data;
IsCompleted = true;
var child = ChildCondition?.Invoke(this);
if (child == null)
return BlockResult.Completed();
return BlockResult.Completed("Reached end of the pipeline");
if (child == this)
return BlockResult.Error("Child is current block");
if (!child.SetParent(this))
return BlockResult.Error("Unable to set child's parent");
return await child.ExecuteAsync(cancellationToken);
return BlockResult.Execute<IExecutableBlock>(child);
}

void IParentBlock.Reset()
Expand All @@ -108,15 +130,17 @@ bool IChildBlock.SetParent(IParentBlock? parent)
return true;
}

private async Task<BlockResult> SkipAsync(CancellationToken cancellationToken)
private BlockResult Skip()
{
(this as IParentBlock).Reset();
var child = ChildCondition?.Invoke(this);
if (child == null)
return BlockResult.Completed();
if (child == this || !child.SetParent(_parent))
return BlockResult.Error("");
return await child.ExecuteAsync(cancellationToken);
return BlockResult.Completed("Reached end of the pipeline");
if (child == this)
return BlockResult.Error("Child is current block");
if(!child.SetParent(_parent))
return BlockResult.Error("Unable to set child's parent");
return BlockResult.Execute<IExecutableBlock>(child);
}

bool IParentBlock.SetChild(Func<IBlock, IChildBlock?> setter)
Expand Down
5 changes: 5 additions & 0 deletions PipelineBlocks/Models/PipelineModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ bool IChildBlock.SetParent(IParentBlock? parent)
{
return startBlock.SetParent(parent);
}

Task<BlockResult> IExecutableBlock.ExecuteSelfAsync(CancellationToken cancellationToken)
{
return startBlock.ExecuteSelfAsync(cancellationToken);
}
}

public class PipelineModule<T>(IChildBlock startBlock, IParentBlock<T> endBlock) : PipelineModule(startBlock, endBlock), IPipelineModule<T>
Expand Down
Loading

0 comments on commit 4c1f6d9

Please sign in to comment.