Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using JetBrains.Annotations;
using System.Diagnostics;
using JetBrains.Annotations;

namespace CreativeCoders.ProcessUtils.Execution;

Expand All @@ -14,6 +15,10 @@ public interface IProcessExecutorBuilder<T>
IProcessExecutorBuilder<T> SetOutputParser<TParser>(Action<TParser>? configure = null)
where TParser : IProcessOutputParser<T>, new();

IProcessExecutorBuilder<T> SetupStartInfo(Action<ProcessStartInfo> configure);

IProcessExecutorBuilder<T> ShouldThrowOnError(bool throwOnError = true);

IProcessExecutor<T> Build();
}

Expand All @@ -24,5 +29,9 @@ public interface IProcessExecutorBuilder

IProcessExecutorBuilder SetArguments(string[] arguments);

IProcessExecutorBuilder SetupStartInfo(Action<ProcessStartInfo> configure);

IProcessExecutorBuilder ShouldThrowOnError(bool throwOnError = true);

IProcessExecutor Build();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace CreativeCoders.ProcessUtils.Execution;

public class ProcessExecutionFailedException(int exitCode, string? errorOutput, string? message = null)
: Exception(message ?? $"Process execution failed with exit code {exitCode}. Error output: {errorOutput}")
{
public int ExitCode { get; } = exitCode;

public string ErrorOutput { get; } = errorOutput ?? string.Empty;
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ private IProcess ExecuteEx(string[]? args, IDictionary<string, object?>? placeho

process.WaitForExit();

CheckThrowOnError(process, true);

return process;
}

Expand All @@ -68,6 +70,8 @@ private async Task<IProcess> ExecuteExAsync(string[]? args, IDictionary<string,

await process.WaitForExitAsync().ConfigureAwait(false);

await CheckThrowOnErrorAsync(process, true).ConfigureAwait(false);

return process;
}

Expand Down Expand Up @@ -168,6 +172,8 @@ public class ProcessExecutor<T>(ProcessExecutorInfo<T> processExecutorInfo, IPro

process.WaitForExit();

CheckThrowOnError(process, true);

return new ProcessExecutionResult<T?>(process, _outputParser.ParseOutput(output));
}

Expand All @@ -189,6 +195,8 @@ public class ProcessExecutor<T>(ProcessExecutorInfo<T> processExecutorInfo, IPro

await process.WaitForExitAsync().ConfigureAwait(false);

await CheckThrowOnErrorAsync(process, true).ConfigureAwait(false);

return new ProcessExecutionResult<T?>(process, _outputParser.ParseOutput(output));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ private ProcessStartInfo CreateProcessStartInfo(string[]? args,
CreateNoWindow = true
};

_processExecutorInfo.ConfigureStartInfo?.Invoke(startupInfo);

return startupInfo;
}

Expand Down Expand Up @@ -53,4 +55,40 @@ protected IProcess StartProcess(string[]? args = null,

return process ?? throw new InvalidOperationException("Failed to start process.");
}

protected void CheckThrowOnError(IProcess process, bool disposeProcessOnThrow)
{
if (!_processExecutorInfo.ThrowOnError || process.ExitCode == 0)
{
return;
}

var exitCode = process.ExitCode;
var standardErrorOutput = process.StandardError.ReadToEnd();

if (disposeProcessOnThrow)
{
process.Dispose();
}

throw new ProcessExecutionFailedException(exitCode, standardErrorOutput);
}

protected async Task CheckThrowOnErrorAsync(IProcess process, bool disposeProcessOnThrow)
{
if (!_processExecutorInfo.ThrowOnError || process.ExitCode == 0)
{
return;
}

var exitCode = process.ExitCode;
var standardErrorOutput = await process.StandardError.ReadToEndAsync().ConfigureAwait(false);

if (disposeProcessOnThrow)
{
process.Dispose();
}

throw new ProcessExecutionFailedException(exitCode, standardErrorOutput);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CreativeCoders.Core;
using System.Diagnostics;
using CreativeCoders.Core;
using CreativeCoders.ProcessUtils.Execution.Parsers;

namespace CreativeCoders.ProcessUtils.Execution;
Expand All @@ -22,6 +23,20 @@ public IProcessExecutorBuilder SetArguments(string[] arguments)
return this;
}

public IProcessExecutorBuilder SetupStartInfo(Action<ProcessStartInfo> configure)
{
ConfigureStartInfo = configure;

return this;
}

public IProcessExecutorBuilder ShouldThrowOnError(bool throwOnError = true)
{
ThrowOnError = throwOnError;

return this;
}

public IProcessExecutor Build()
{
if (string.IsNullOrWhiteSpace(FileName))
Expand All @@ -31,8 +46,11 @@ public IProcessExecutor Build()

var executorInfo = new ProcessExecutorInfo(
FileName,
Arguments ?? [],
UsePlaceholderVars);
Arguments ?? [])
{
ConfigureStartInfo = ConfigureStartInfo,
ThrowOnError = ThrowOnError
};

return new ProcessExecutor(executorInfo, _processFactory);
}
Expand Down Expand Up @@ -78,6 +96,20 @@ public IProcessExecutorBuilder<T> SetOutputParser<TParser>(Action<TParser>? conf
return this;
}

public IProcessExecutorBuilder<T> SetupStartInfo(Action<ProcessStartInfo> configure)
{
ConfigureStartInfo = configure;

return this;
}

public IProcessExecutorBuilder<T> ShouldThrowOnError(bool throwOnError = true)
{
ThrowOnError = throwOnError;

return this;
}

private void ReturnOutputAsText()
{
if (typeof(T) != typeof(string))
Expand Down Expand Up @@ -108,9 +140,12 @@ public IProcessExecutor<T> Build()
var executorInfo = new ProcessExecutorInfo<T>(
FileName,
Arguments ?? [],
UsePlaceholderVars,
_outputParser ??
throw new InvalidOperationException("OutputParser must be set before building the executor."));
throw new InvalidOperationException("OutputParser must be set before building the executor."))
{
ConfigureStartInfo = ConfigureStartInfo,
ThrowOnError = ThrowOnError
};

return new ProcessExecutor<T>(executorInfo, _processFactory);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
namespace CreativeCoders.ProcessUtils.Execution;
using System.Diagnostics;

namespace CreativeCoders.ProcessUtils.Execution;

public abstract class ProcessExecutorBuilderBase
{
protected string? FileName { get; set; }

protected string[]? Arguments { get; set; }

protected bool UsePlaceholderVars { get; set; }
protected bool ThrowOnError { get; set; }

protected Action<ProcessStartInfo>? ConfigureStartInfo { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
namespace CreativeCoders.ProcessUtils.Execution;
using System.Diagnostics;

namespace CreativeCoders.ProcessUtils.Execution;

public class ProcessExecutorInfo<T>(
string fileName,
string[] arguments,
bool usePlaceholderVars,
IProcessOutputParser<T> outputParser)
: ProcessExecutorInfo(fileName, arguments, usePlaceholderVars)
: ProcessExecutorInfo(fileName, arguments)
{
public IProcessOutputParser<T> OutputParser { get; set; } = outputParser;
}

public class ProcessExecutorInfo(string fileName, string[] arguments, bool usePlaceholderVars)
public class ProcessExecutorInfo(string fileName, string[] arguments)
{
public string FileName { get; } = fileName;

public string[] Arguments { get; } = arguments;

public Action<ProcessStartInfo>? ConfigureStartInfo { get; set; }

public bool ThrowOnError { get; set; }

public bool RedirectStandardOutput { get; set; } = true;

public bool RedirectStandardError { get; set; } = true;
Expand Down
Loading
Loading