mfgames-cil/src/MfGames.Nitride.Exec/ExecOperation.cs

113 lines
3.2 KiB
C#

using System.Runtime.CompilerServices;
using CliWrap;
using CliWrap.Buffered;
using FluentValidation;
using MfGames.Gallium;
using MfGames.Nitride.Generators;
using Serilog;
// ReSharper disable ClassNeverInstantiated.Global
namespace MfGames.Nitride.Exec;
/// <summary>
/// An operation that wraps around CliWrap to run an executable.
/// </summary>
[WithProperties]
public partial class ExecOperation : AsyncOperationBase
{
private readonly ILogger logger;
private readonly IValidator<ExecOperation> validator;
public ExecOperation(ILogger logger, IValidator<ExecOperation> validator)
{
this.logger = logger.ForContext<ExecOperation>();
this.validator = validator;
}
/// <summary>
/// Gets or sets the command associated with this operation.
/// </summary>
public Func<Command>? CreateCommand { get; set; }
/// <summary>
/// Gets or sets a callback to process the buffered output.
/// </summary>
/// <remarks>
/// This is mutually exclusive with OnResult.
/// </remarks>
public Func<BufferedCommandResult, IEnumerable<Entity>>? OnBufferedResult { get; set; }
/// <summary>
/// Gets or sets a callback to process the output.
/// </summary>
/// <remarks>
/// This is mutually exclusive with OnBufferedResult.
/// </remarks>
public Func<CommandResult, IEnumerable<Entity>>? OnResult { get; set; }
/// <inheritdoc />
public override async IAsyncEnumerable<Entity> RunAsync(
IAsyncEnumerable<Entity> input,
[EnumeratorCancellation] CancellationToken cancellationToken = default
)
{
// Make sure everything is validated.
await this.validator.ValidateAndThrowAsync(this, cancellationToken);
// Drain the inputs.
await foreach (Entity item in input.WithCancellation(cancellationToken))
{
yield return item;
}
// Create the command from the input, then execute it as a buffered
// output if we have a buffered result callback, otherwise as a command
// result.
Command command = this.CreateCommand!();
if (this.OnBufferedResult != null)
{
BufferedCommandResult result = await command.ExecuteBufferedAsync(cancellationToken);
this.logger.Debug(
"Execute buffered: {Command} {Arguments} = {ExitCode}",
command.TargetFilePath,
command.Arguments,
result.ExitCode
);
IEnumerable<Entity> list = this.OnBufferedResult(result);
foreach (Entity item in list)
{
yield return item;
}
}
else
{
CommandResult result = await command.ExecuteAsync(cancellationToken);
this.logger.Debug(
"Execute: {Command} {Arguments} = {ExitCode}",
command.TargetFilePath,
command.Arguments,
result.ExitCode
);
IEnumerable<Entity>? list = this.OnResult?.Invoke(result);
if (list == null)
{
yield break;
}
foreach (Entity item in list)
{
yield return item;
}
}
}
}