diff --git a/Core.sln.DotSettings b/Core.sln.DotSettings
index c36b219f..85a2d11f 100644
--- a/Core.sln.DotSettings
+++ b/Core.sln.DotSettings
@@ -10,6 +10,7 @@
SUGGESTION
SUGGESTION
SUGGESTION
+ DO_NOT_SHOW
WARNING
DO_NOT_SHOW
WARNING
diff --git a/samples/CliHostSampleApp/Program.cs b/samples/CliHostSampleApp/Program.cs
index 7b535186..8414e26e 100644
--- a/samples/CliHostSampleApp/Program.cs
+++ b/samples/CliHostSampleApp/Program.cs
@@ -14,6 +14,10 @@ private static ICliHost CreateCliHost()
{
return CliHostBuilder.Create()
.EnableHelp(HelpCommandKind.CommandOrArgument)
+ .PrintHeaderText(["[red]This is a sample cli host[/]"])
+ .PrintHeaderMarkup(["[green]This is a second pre processor[/]"])
+ .PrintFooterText(["Bye bye"])
+ .PrintFooterMarkup(["[red]This is a second post processor[/]"])
.Build();
}
}
diff --git a/source/Cli/CreativeCoders.Cli.Core/CliCommandAttribute.cs b/source/Cli/CreativeCoders.Cli.Core/CliCommandAttribute.cs
index 56471762..f19a08ee 100644
--- a/source/Cli/CreativeCoders.Cli.Core/CliCommandAttribute.cs
+++ b/source/Cli/CreativeCoders.Cli.Core/CliCommandAttribute.cs
@@ -12,6 +12,4 @@ public class CliCommandAttribute(string[] commands) : Attribute
public string Description { get; set; } = string.Empty;
public string[] AlternativeCommands { get; init; } = [];
-
- public bool IsDefaultCommand { get; set; }
}
diff --git a/source/Cli/CreativeCoders.Cli.Core/CliProcessorExecutionCondition.cs b/source/Cli/CreativeCoders.Cli.Core/CliProcessorExecutionCondition.cs
new file mode 100644
index 00000000..5b326e1a
--- /dev/null
+++ b/source/Cli/CreativeCoders.Cli.Core/CliProcessorExecutionCondition.cs
@@ -0,0 +1,8 @@
+namespace CreativeCoders.Cli.Core;
+
+public enum CliProcessorExecutionCondition
+{
+ Always,
+ OnlyOnHelp,
+ OnlyOnCommand
+}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/CliResult.cs b/source/Cli/CreativeCoders.Cli.Core/CliResult.cs
similarity index 77%
rename from source/Cli/CreativeCoders.Cli.Hosting/CliResult.cs
rename to source/Cli/CreativeCoders.Cli.Core/CliResult.cs
index a7e70592..5748f546 100644
--- a/source/Cli/CreativeCoders.Cli.Hosting/CliResult.cs
+++ b/source/Cli/CreativeCoders.Cli.Core/CliResult.cs
@@ -1,6 +1,6 @@
using JetBrains.Annotations;
-namespace CreativeCoders.Cli.Hosting;
+namespace CreativeCoders.Cli.Core;
[PublicAPI]
public class CliResult(int exitCode)
diff --git a/source/Cli/CreativeCoders.Cli.Core/ICliPostProcessor.cs b/source/Cli/CreativeCoders.Cli.Core/ICliPostProcessor.cs
new file mode 100644
index 00000000..7348bf0c
--- /dev/null
+++ b/source/Cli/CreativeCoders.Cli.Core/ICliPostProcessor.cs
@@ -0,0 +1,11 @@
+using JetBrains.Annotations;
+
+namespace CreativeCoders.Cli.Core;
+
+[PublicAPI]
+public interface ICliPostProcessor
+{
+ Task ExecuteAsync(CliResult cliResult);
+
+ CliProcessorExecutionCondition ExecutionCondition { get; }
+}
diff --git a/source/Cli/CreativeCoders.Cli.Core/ICliPreProcessor.cs b/source/Cli/CreativeCoders.Cli.Core/ICliPreProcessor.cs
new file mode 100644
index 00000000..29a5bc6a
--- /dev/null
+++ b/source/Cli/CreativeCoders.Cli.Core/ICliPreProcessor.cs
@@ -0,0 +1,11 @@
+using JetBrains.Annotations;
+
+namespace CreativeCoders.Cli.Core;
+
+[PublicAPI]
+public interface ICliPreProcessor
+{
+ Task ExecuteAsync(string[] args);
+
+ CliProcessorExecutionCondition ExecutionCondition { get; }
+}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/CliExitCodes.cs b/source/Cli/CreativeCoders.Cli.Hosting/CliExitCodes.cs
index 1a5a1832..b7461b6c 100644
--- a/source/Cli/CreativeCoders.Cli.Hosting/CliExitCodes.cs
+++ b/source/Cli/CreativeCoders.Cli.Hosting/CliExitCodes.cs
@@ -11,4 +11,8 @@ public static class CliExitCodes
public const int CommandResultUnknown = int.MinValue + 2;
public const int CommandOptionsInvalid = int.MinValue + 3;
+
+ public const int PreProcessorFailed = int.MinValue + 4;
+
+ public const int PostProcessorFailed = int.MinValue + 5;
}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/CliHostBuilderExtensions.cs b/source/Cli/CreativeCoders.Cli.Hosting/CliHostBuilderExtensions.cs
new file mode 100644
index 00000000..21bfb4f7
--- /dev/null
+++ b/source/Cli/CreativeCoders.Cli.Hosting/CliHostBuilderExtensions.cs
@@ -0,0 +1,125 @@
+using System.Diagnostics.CodeAnalysis;
+using CreativeCoders.Cli.Core;
+using CreativeCoders.Cli.Hosting.PreProcessors;
+
+namespace CreativeCoders.Cli.Hosting;
+
+[ExcludeFromCodeCoverage]
+public static class CliHostBuilderExtensions
+{
+ ///
+ /// Registers a pre-processor to print a header as plain text. The header is displayed when the CLI application
+ /// is executed, before the command processing, based on the specified execution condition.
+ ///
+ ///
+ /// The instance to which the pre-processor is being added.
+ ///
+ ///
+ /// An enumerable collection of strings representing the lines of text to be displayed in the header.
+ /// Each string represents an individual line.
+ ///
+ ///
+ /// A value from that determines when the header should be displayed.
+ /// Defaults to .
+ ///
+ ///
+ /// The instance for method chaining.
+ ///
+ public static ICliHostBuilder PrintHeaderText(this ICliHostBuilder builder, IEnumerable lines,
+ CliProcessorExecutionCondition executionCondition = CliProcessorExecutionCondition.Always)
+ {
+ return builder.RegisterPreProcessor(x =>
+ {
+ x.Lines = lines;
+ x.PlainText = true;
+ x.ExecutionCondition = executionCondition;
+ });
+ }
+
+ ///
+ /// Registers a pre-processor to print a header using markup text. The header is displayed during the CLI application
+ /// execution, before the command processing, based on the specified execution condition.
+ ///
+ ///
+ /// The instance to which the pre-processor is being added.
+ ///
+ ///
+ /// An enumerable collection of strings representing the lines of markup text to be displayed in the header.
+ /// The markup can include formatting instructions that are interpreted during rendering.
+ ///
+ ///
+ /// A value from that determines when the header should be displayed.
+ /// Defaults to .
+ ///
+ ///
+ /// The instance for method chaining.
+ ///
+ public static ICliHostBuilder PrintHeaderMarkup(this ICliHostBuilder builder, IEnumerable lines,
+ CliProcessorExecutionCondition executionCondition = CliProcessorExecutionCondition.Always)
+ {
+ return builder.RegisterPreProcessor(x =>
+ {
+ x.Lines = lines;
+ x.PlainText = false;
+ x.ExecutionCondition = executionCondition;
+ });
+ }
+
+ ///
+ /// Registers a post-processor to print a footer as plain text. The footer is displayed after the command processing
+ /// is complete, based on the specified execution condition.
+ ///
+ ///
+ /// The instance to which the post-processor is being added.
+ ///
+ ///
+ /// An enumerable collection of strings representing the lines of text to be displayed in the footer.
+ /// Each string represents an individual line.
+ ///
+ ///
+ /// A value from that determines when the footer should be displayed.
+ /// Defaults to .
+ ///
+ ///
+ /// The instance for method chaining.
+ ///
+ public static ICliHostBuilder PrintFooterText(this ICliHostBuilder builder, IEnumerable lines,
+ CliProcessorExecutionCondition executionCondition = CliProcessorExecutionCondition.Always)
+ {
+ return builder.RegisterPostProcessor(x =>
+ {
+ x.Lines = lines;
+ x.PlainText = true;
+ x.ExecutionCondition = executionCondition;
+ });
+ }
+
+ ///
+ /// Registers a post-processor to print a footer using markup formatting. The footer is displayed
+ /// after the command execution, based on the specified execution condition.
+ ///
+ ///
+ /// The instance to which the post-processor is being added.
+ ///
+ ///
+ /// An enumerable collection of strings representing the lines of footer text to be displayed.
+ /// Each string supports markup formatting for better visual representation.
+ ///
+ ///
+ /// A value from that determines when the footer should be displayed.
+ /// Defaults to .
+ ///
+ ///
+ /// The instance for method chaining.
+ ///
+ public static ICliHostBuilder PrintFooterMarkup(this ICliHostBuilder builder, IEnumerable lines,
+ CliProcessorExecutionCondition executionCondition = CliProcessorExecutionCondition.Always)
+ {
+ return builder.RegisterPostProcessor(x =>
+ {
+ x.Lines = lines;
+ x.PlainText = false;
+ x.ExecutionCondition = executionCondition;
+ });
+ }
+}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/CliHostSettings.cs b/source/Cli/CreativeCoders.Cli.Hosting/CliHostSettings.cs
new file mode 100644
index 00000000..f8ea4728
--- /dev/null
+++ b/source/Cli/CreativeCoders.Cli.Hosting/CliHostSettings.cs
@@ -0,0 +1,6 @@
+namespace CreativeCoders.Cli.Hosting;
+
+public class CliHostSettings
+{
+ public bool UseValidation { get; init; }
+}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/DefaultCliHost.cs b/source/Cli/CreativeCoders.Cli.Hosting/DefaultCliHost.cs
index c1484791..87cd1c6d 100644
--- a/source/Cli/CreativeCoders.Cli.Hosting/DefaultCliHost.cs
+++ b/source/Cli/CreativeCoders.Cli.Hosting/DefaultCliHost.cs
@@ -17,7 +17,9 @@ public class DefaultCliHost(
IAnsiConsole ansiConsole,
ICliCommandStore commandStore,
IServiceProvider serviceProvider,
- ICliCommandHelpHandler commandHelpHandler)
+ ICliCommandHelpHandler commandHelpHandler,
+ IEnumerable preProcessors,
+ IEnumerable postProcessors)
: ICliHost
{
private readonly IServiceProvider _serviceProvider = Ensure.NotNull(serviceProvider);
@@ -110,18 +112,26 @@ public async Task RunAsync(string[] args)
{
if (_commandHelpHandler.ShouldPrintHelp(args))
{
+ await ExecuteHelpPreProcessorsAsync(args).ConfigureAwait(false);
+
_commandHelpHandler.PrintHelp(args);
- return new CliResult(CliExitCodes.Success);
+ var cliHelpResult = new CliResult(CliExitCodes.Success);
+
+ return await ExecuteHelpPostProcessorsAsync(cliHelpResult).ConfigureAwait(false);
}
+ await ExecuteCommandPreProcessorsAsync(args).ConfigureAwait(false);
+
var (command, optionsArgs, commandInfo) = CreateCliCommand(args);
var commandContext = _serviceProvider.GetRequiredService();
commandContext.AllArgs = args;
commandContext.OptionsArgs = optionsArgs;
- return await ExecuteAsync(commandInfo, command, optionsArgs).ConfigureAwait(false);
+ var cliResult = await ExecuteAsync(commandInfo, command, optionsArgs).ConfigureAwait(false);
+
+ return await ExecuteCommandPostProcessorsAsync(cliResult).ConfigureAwait(false);
}
catch (CliCommandConstructionFailedException e)
{
@@ -153,6 +163,83 @@ public async Task RunAsync(string[] args)
return new CliResult(e.ExitCode);
}
+ catch (CliExitException e)
+ {
+ _ansiConsole.MarkupLine($"[red]{e.Message}[/]");
+
+ return new CliResult(e.ExitCode);
+ }
+ }
+
+ private async Task ExecuteHelpPreProcessorsAsync(string[] args)
+ {
+ CliProcessorExecutionCondition[] conditions =
+ [CliProcessorExecutionCondition.OnlyOnHelp, CliProcessorExecutionCondition.Always];
+
+ await ExecutePreProcessorsAsync(preProcessors, conditions, args)
+ .ConfigureAwait(false);
+ }
+
+ private async Task ExecuteCommandPreProcessorsAsync(string[] args)
+ {
+ CliProcessorExecutionCondition[] conditions =
+ [CliProcessorExecutionCondition.OnlyOnCommand, CliProcessorExecutionCondition.Always];
+
+ await ExecutePreProcessorsAsync(preProcessors, conditions, args)
+ .ConfigureAwait(false);
+ }
+
+ private static async Task ExecutePreProcessorsAsync(IEnumerable preProcessors,
+ CliProcessorExecutionCondition[] conditions, string[] args)
+ {
+ foreach (var preProcessor in preProcessors.Where(x => conditions.Contains(x.ExecutionCondition)))
+ {
+ try
+ {
+ await preProcessor.ExecuteAsync(args).ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ throw new CliPreProcessorException(preProcessor,
+ $"PreProcessor execution failed: {e.Message}", e);
+ }
+ }
+ }
+
+ private async Task ExecuteCommandPostProcessorsAsync(CliResult cliResult)
+ {
+ CliProcessorExecutionCondition[] conditions =
+ [CliProcessorExecutionCondition.OnlyOnCommand, CliProcessorExecutionCondition.Always];
+
+ return await ExecutePostProcessorsAsync(postProcessors, conditions, cliResult).ConfigureAwait(false);
+ }
+
+ private async Task ExecuteHelpPostProcessorsAsync(CliResult cliResult)
+ {
+ CliProcessorExecutionCondition[] conditions =
+ [CliProcessorExecutionCondition.OnlyOnHelp, CliProcessorExecutionCondition.Always];
+
+ return await ExecutePostProcessorsAsync(postProcessors, conditions, cliResult).ConfigureAwait(false);
+ }
+
+ private static async Task ExecutePostProcessorsAsync(
+ IEnumerable postProcessors, CliProcessorExecutionCondition[] conditions,
+ CliResult cliResult)
+ {
+ foreach (var postProcessor in postProcessors.Where(x => conditions.Contains(x.ExecutionCondition)))
+ {
+ try
+ {
+ await postProcessor.ExecuteAsync(cliResult).ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ throw new CliPostProcessorException(postProcessor,
+ $"PostProcessor execution failed: {e.Message}", e);
+ }
+ }
+
+ return cliResult;
}
///
@@ -181,8 +268,3 @@ private void PrintNearestMatch(string[] args)
_commandHelpHandler.PrintHelpFor(findCommandGroupNodeResult.Node.ChildNodes);
}
}
-
-public class CliHostSettings
-{
- public bool UseValidation { get; init; }
-}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/DefaultCliHostBuilder.cs b/source/Cli/CreativeCoders.Cli.Hosting/DefaultCliHostBuilder.cs
index 18b7226a..ba16ac83 100644
--- a/source/Cli/CreativeCoders.Cli.Hosting/DefaultCliHostBuilder.cs
+++ b/source/Cli/CreativeCoders.Cli.Hosting/DefaultCliHostBuilder.cs
@@ -19,7 +19,7 @@ public class DefaultCliHostBuilder : ICliHostBuilder
private bool _helpEnabled;
- private HelpCommandKind _helpCommandKind;
+ private HelpCommandKind[] _helpCommandKinds = [];
private bool _skipScanEntryAssembly;
@@ -65,10 +65,10 @@ public ICliHostBuilder ScanAssemblies(params Assembly[] assemblies)
return this;
}
- public ICliHostBuilder EnableHelp(HelpCommandKind commandKind)
+ public ICliHostBuilder EnableHelp(params HelpCommandKind[] commandKinds)
{
_helpEnabled = true;
- _helpCommandKind = commandKind;
+ _helpCommandKinds = commandKinds;
return this;
}
@@ -87,6 +87,42 @@ public ICliHostBuilder SkipScanEntryAssembly(bool skipScanEntryAssembly = true)
return this;
}
+ public ICliHostBuilder RegisterPreProcessor(Action? configure = null)
+ where T : class, ICliPreProcessor
+ {
+ if (configure != null)
+ {
+ return ConfigureServices(x => x.AddSingleton(sp =>
+ {
+ var preProcessor = sp.GetServiceOrCreateInstance();
+
+ configure(preProcessor);
+
+ return preProcessor;
+ }));
+ }
+
+ return ConfigureServices(x => x.AddSingleton());
+ }
+
+ public ICliHostBuilder RegisterPostProcessor(Action? configure = null)
+ where T : class, ICliPostProcessor
+ {
+ if (configure != null)
+ {
+ return ConfigureServices(x => x.AddSingleton(sp =>
+ {
+ var postProcessor = sp.GetServiceOrCreateInstance();
+
+ configure(postProcessor);
+
+ return postProcessor;
+ }));
+ }
+
+ return ConfigureServices(x => x.AddSingleton());
+ }
+
[SuppressMessage("Performance", "CA1859:Use concrete types when possible for improved performance")]
private IServiceProvider BuildServiceProvider()
{
@@ -96,7 +132,7 @@ private IServiceProvider BuildServiceProvider()
{
services.TryAddSingleton(_ => new HelpHandlerSettings
{
- CommandKind = _helpCommandKind
+ CommandKinds = _helpCommandKinds
});
services.TryAddSingleton();
}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/Exceptions/CliCommandAbortException.cs b/source/Cli/CreativeCoders.Cli.Hosting/Exceptions/CliCommandAbortException.cs
index 78f71a23..fef13f6f 100644
--- a/source/Cli/CreativeCoders.Cli.Hosting/Exceptions/CliCommandAbortException.cs
+++ b/source/Cli/CreativeCoders.Cli.Hosting/Exceptions/CliCommandAbortException.cs
@@ -1,5 +1,8 @@
+using JetBrains.Annotations;
+
namespace CreativeCoders.Cli.Hosting.Exceptions;
+[PublicAPI]
public class CliCommandAbortException(string message, int exitCode, Exception? exception = null)
: CliExitException(message, exitCode, exception)
{
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/Exceptions/CliPostProcessorException.cs b/source/Cli/CreativeCoders.Cli.Hosting/Exceptions/CliPostProcessorException.cs
new file mode 100644
index 00000000..c76898f7
--- /dev/null
+++ b/source/Cli/CreativeCoders.Cli.Hosting/Exceptions/CliPostProcessorException.cs
@@ -0,0 +1,11 @@
+using CreativeCoders.Cli.Core;
+
+namespace CreativeCoders.Cli.Hosting.Exceptions;
+
+public class CliPostProcessorException(
+ ICliPostProcessor postProcessor,
+ string message,
+ Exception? innerException) : CliExitException(message, CliExitCodes.PostProcessorFailed, innerException)
+{
+ public ICliPostProcessor PostProcessor { get; } = postProcessor;
+}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/Exceptions/CliPreProcessorException.cs b/source/Cli/CreativeCoders.Cli.Hosting/Exceptions/CliPreProcessorException.cs
new file mode 100644
index 00000000..a0d6ceff
--- /dev/null
+++ b/source/Cli/CreativeCoders.Cli.Hosting/Exceptions/CliPreProcessorException.cs
@@ -0,0 +1,12 @@
+using CreativeCoders.Cli.Core;
+
+namespace CreativeCoders.Cli.Hosting.Exceptions;
+
+public class CliPreProcessorException(
+ ICliPreProcessor preProcessor,
+ string message,
+ Exception? innerException)
+ : CliExitException(message, CliExitCodes.PreProcessorFailed, innerException)
+{
+ public ICliPreProcessor PreProcessor { get; } = preProcessor;
+}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/Help/CliCommandHelpHandler.cs b/source/Cli/CreativeCoders.Cli.Hosting/Help/CliCommandHelpHandler.cs
index 1e2b02fc..6c6e67db 100644
--- a/source/Cli/CreativeCoders.Cli.Hosting/Help/CliCommandHelpHandler.cs
+++ b/source/Cli/CreativeCoders.Cli.Hosting/Help/CliCommandHelpHandler.cs
@@ -28,12 +28,18 @@ public bool ShouldPrintHelp(string[] args)
{
var lowerCaseArgs = args.Select(x => x.ToLower()).ToArray();
- return _settings.CommandKind switch
+ return _settings.CommandKinds.Any(x => ShouldPrintHelpFor(x, lowerCaseArgs));
+ }
+
+ private static bool ShouldPrintHelpFor(HelpCommandKind helpCommandKind, string[] args)
+ {
+ return helpCommandKind switch
{
- HelpCommandKind.Command => lowerCaseArgs.FirstOrDefault() == "help",
- HelpCommandKind.Argument => lowerCaseArgs.Contains("--help"),
- HelpCommandKind.CommandOrArgument => lowerCaseArgs.FirstOrDefault() == "help" ||
- lowerCaseArgs.Contains("--help"),
+ HelpCommandKind.Command => args.FirstOrDefault() == "help",
+ HelpCommandKind.Argument => args.Contains("--help"),
+ HelpCommandKind.EmptyArgs => args.Length == 0,
+ HelpCommandKind.CommandOrArgument => args.FirstOrDefault() == "help" ||
+ args.Contains("--help"),
_ => false
};
}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/Help/HelpCommandKind.cs b/source/Cli/CreativeCoders.Cli.Hosting/Help/HelpCommandKind.cs
index c929ce62..169bcfb9 100644
--- a/source/Cli/CreativeCoders.Cli.Hosting/Help/HelpCommandKind.cs
+++ b/source/Cli/CreativeCoders.Cli.Hosting/Help/HelpCommandKind.cs
@@ -4,5 +4,6 @@ public enum HelpCommandKind
{
Command,
Argument,
+ EmptyArgs,
CommandOrArgument
}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/Help/HelpHandlerSettings.cs b/source/Cli/CreativeCoders.Cli.Hosting/Help/HelpHandlerSettings.cs
index ab7568bd..3a47678e 100644
--- a/source/Cli/CreativeCoders.Cli.Hosting/Help/HelpHandlerSettings.cs
+++ b/source/Cli/CreativeCoders.Cli.Hosting/Help/HelpHandlerSettings.cs
@@ -2,5 +2,5 @@ namespace CreativeCoders.Cli.Hosting.Help;
public class HelpHandlerSettings
{
- public HelpCommandKind CommandKind { get; init; }
+ public HelpCommandKind[] CommandKinds { get; init; } = [];
}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/ICliHost.cs b/source/Cli/CreativeCoders.Cli.Hosting/ICliHost.cs
index 9a364536..4dce2bf5 100644
--- a/source/Cli/CreativeCoders.Cli.Hosting/ICliHost.cs
+++ b/source/Cli/CreativeCoders.Cli.Hosting/ICliHost.cs
@@ -1,3 +1,4 @@
+using CreativeCoders.Cli.Core;
using JetBrains.Annotations;
namespace CreativeCoders.Cli.Hosting;
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/ICliHostBuilder.cs b/source/Cli/CreativeCoders.Cli.Hosting/ICliHostBuilder.cs
index 657959fa..6dca7d9a 100644
--- a/source/Cli/CreativeCoders.Cli.Hosting/ICliHostBuilder.cs
+++ b/source/Cli/CreativeCoders.Cli.Hosting/ICliHostBuilder.cs
@@ -49,12 +49,12 @@ ICliHostBuilder UseContext(Action? configu
///
/// Enables help functionality for the CLI application by specifying the type of help commands to be supported.
///
- ///
+ ///
/// Defines the type of help commands that can be used within the application.
/// This can be a command-specific help, argument-specific help, or both, as specified by the values in .
///
/// The same instance to allow for method chaining.
- ICliHostBuilder EnableHelp(HelpCommandKind commandKind);
+ ICliHostBuilder EnableHelp(params HelpCommandKind[] commandKinds);
///
/// Enables or disables command options validation.
@@ -74,6 +74,27 @@ ICliHostBuilder UseContext(Action? configu
/// The same instance.
ICliHostBuilder SkipScanEntryAssembly(bool skipScanEntryAssembly = true);
+ ///
+ /// Registers a pre-processor for the CLI application.
+ ///
+ /// The type of the pre-processor to be registered. Must implement .
+ ///
+ /// An optional action to configure the instance of . The action receives
+ /// the instance as a parameter.
+ ///
+ /// The same instance.
+ ICliHostBuilder RegisterPreProcessor(Action? configure = null) where T : class, ICliPreProcessor;
+
+ ///
+ /// Registers a post-processor to be executed after the CLI command is processed.
+ ///
+ /// The type of the post-processor that implements .
+ ///
+ /// An optional action to configure the post-processor instance. The action receives a single parameter of type .
+ ///
+ /// The same instance.
+ ICliHostBuilder RegisterPostProcessor(Action? configure = null) where T : class, ICliPostProcessor;
+
///
/// Builds and creates an instance of configured through the current builder.
///
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/PreProcessors/PrintFooterPostProcessor.cs b/source/Cli/CreativeCoders.Cli.Hosting/PreProcessors/PrintFooterPostProcessor.cs
new file mode 100644
index 00000000..1153f950
--- /dev/null
+++ b/source/Cli/CreativeCoders.Cli.Hosting/PreProcessors/PrintFooterPostProcessor.cs
@@ -0,0 +1,40 @@
+using System.Diagnostics.CodeAnalysis;
+using CreativeCoders.Cli.Core;
+using CreativeCoders.Core;
+using JetBrains.Annotations;
+using Spectre.Console;
+
+namespace CreativeCoders.Cli.Hosting.PreProcessors;
+
+[UsedImplicitly]
+[ExcludeFromCodeCoverage]
+public class PrintFooterPostProcessor(IAnsiConsole ansiConsole) : ICliPostProcessor
+{
+ private readonly IAnsiConsole _ansiConsole = Ensure.NotNull(ansiConsole);
+
+ public Task ExecuteAsync(CliResult cliResult)
+ {
+ if (PlainText)
+ {
+ foreach (var line in Lines)
+ {
+ _ansiConsole.WriteLine(line);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ foreach (var line in Lines)
+ {
+ _ansiConsole.MarkupLine(line);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ public CliProcessorExecutionCondition ExecutionCondition { get; set; }
+
+ public IEnumerable Lines { get; set; } = [];
+
+ public bool PlainText { get; set; }
+}
diff --git a/source/Cli/CreativeCoders.Cli.Hosting/PreProcessors/PrintHeaderPreProcessor.cs b/source/Cli/CreativeCoders.Cli.Hosting/PreProcessors/PrintHeaderPreProcessor.cs
new file mode 100644
index 00000000..4feefd99
--- /dev/null
+++ b/source/Cli/CreativeCoders.Cli.Hosting/PreProcessors/PrintHeaderPreProcessor.cs
@@ -0,0 +1,40 @@
+using System.Diagnostics.CodeAnalysis;
+using CreativeCoders.Cli.Core;
+using CreativeCoders.Core;
+using JetBrains.Annotations;
+using Spectre.Console;
+
+namespace CreativeCoders.Cli.Hosting.PreProcessors;
+
+[UsedImplicitly]
+[ExcludeFromCodeCoverage]
+public class PrintHeaderPreProcessor(IAnsiConsole ansiConsole) : ICliPreProcessor
+{
+ private readonly IAnsiConsole _ansiConsole = Ensure.NotNull(ansiConsole);
+
+ public Task ExecuteAsync(string[] args)
+ {
+ if (PlainText)
+ {
+ foreach (var line in Lines)
+ {
+ _ansiConsole.WriteLine(line);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ foreach (var line in Lines)
+ {
+ _ansiConsole.MarkupLine(line);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ public CliProcessorExecutionCondition ExecutionCondition { get; set; }
+
+ public IEnumerable Lines { get; set; } = [];
+
+ public bool PlainText { get; set; }
+}
diff --git a/tests/CreativeCoders.Cli.Tests/Hosting/CliCommandHelpHandlerTests.cs b/tests/CreativeCoders.Cli.Tests/Hosting/CliCommandHelpHandlerTests.cs
index 8800ea2b..76efab75 100644
--- a/tests/CreativeCoders.Cli.Tests/Hosting/CliCommandHelpHandlerTests.cs
+++ b/tests/CreativeCoders.Cli.Tests/Hosting/CliCommandHelpHandlerTests.cs
@@ -16,11 +16,41 @@ namespace CreativeCoders.Cli.Tests.Hosting;
[SuppressMessage("ReSharper", "NullableWarningSuppressionIsUsed")]
public class CliCommandHelpHandlerTests
{
+ [Theory]
+ [InlineData(new[] { HelpCommandKind.Command }, "help", "run")]
+ [InlineData(new[] { HelpCommandKind.Argument }, "run", "--help")]
+ [InlineData(new[] { HelpCommandKind.CommandOrArgument }, "help", "run")]
+ [InlineData(new[] { HelpCommandKind.CommandOrArgument }, "run", "--help")]
+ [InlineData(new[] { HelpCommandKind.Command, HelpCommandKind.Argument }, "help", "run")]
+ [InlineData(new[] { HelpCommandKind.Command, HelpCommandKind.Argument }, "run", "--help")]
+ [InlineData(new[] { HelpCommandKind.EmptyArgs })]
+ [InlineData(new[] { HelpCommandKind.Command, HelpCommandKind.Argument, HelpCommandKind.EmptyArgs },
+ "help", "run")]
+ [InlineData(new[] { HelpCommandKind.Command, HelpCommandKind.Argument, HelpCommandKind.EmptyArgs }, "run",
+ "--help")]
+ [InlineData(new[] { HelpCommandKind.Command, HelpCommandKind.Argument, HelpCommandKind.EmptyArgs })]
+ public void ShouldPrintHelp_DifferentKinds_RespectsHelpCommand(HelpCommandKind[] commandKinds,
+ params string[] args)
+ {
+ // Arrange
+ var handler = CreateHandler(commandKinds, out var stringWriter, out _);
+
+ // Act
+ var resultHelp = handler.ShouldPrintHelp(args);
+
+ // Assert
+ resultHelp
+ .Should()
+ .BeTrue();
+
+ stringWriter.Dispose();
+ }
+
[Fact]
public void ShouldPrintHelp_CommandKindCommand_RespectsHelpCommand()
{
// Arrange
- var handler = CreateHandler(HelpCommandKind.Command, out var stringWriter, out _);
+ var handler = CreateHandler([HelpCommandKind.Command], out var stringWriter, out _);
// Act
var resultHelp = handler.ShouldPrintHelp(["help"]);
@@ -42,7 +72,7 @@ public void ShouldPrintHelp_CommandKindCommand_RespectsHelpCommand()
public void ShouldPrintHelp_CommandKindArgument_RespectsHelpArgument()
{
// Arrange
- var handler = CreateHandler(HelpCommandKind.Argument, out var stringWriter, out _);
+ var handler = CreateHandler([HelpCommandKind.Argument], out var stringWriter, out _);
// Act
var resultHelp = handler.ShouldPrintHelp(["run", "--help"]);
@@ -85,7 +115,7 @@ public void PrintHelp_WhenCommandFound_PrintsDescriptionSyntaxAndOptions()
var commandStore = new CliCommandStore();
commandStore.AddCommands([commandInfo]);
- var helpHandler = CreateHandler(HelpCommandKind.CommandOrArgument, out var writer,
+ var helpHandler = CreateHandler([HelpCommandKind.CommandOrArgument], out var writer,
out var optionsHelpGenerator, commandStore);
A.CallTo(() => optionsHelpGenerator.CreateHelp(typeof(DummyOptions)))
@@ -152,7 +182,7 @@ public void PrintHelp_WhenRootRequested_PrintsGroupsAndCommands()
var commandStore = new CliCommandStore();
commandStore.AddCommands([commandInfoOne, commandInfoTwo]);
- var handler = CreateHandler(HelpCommandKind.CommandOrArgument, out var writer, out _, commandStore);
+ var handler = CreateHandler([HelpCommandKind.CommandOrArgument], out var writer, out _, commandStore);
// Act
handler.PrintHelp([]);
@@ -170,7 +200,7 @@ public void PrintHelp_WhenRootRequested_PrintsGroupsAndCommands()
}
private static CliCommandHelpHandler CreateHandler(
- HelpCommandKind commandKind,
+ HelpCommandKind[] commandKinds,
out StringWriter writer,
out IOptionsHelpGenerator optionsHelpGenerator,
ICliCommandStore? commandStore = null)
@@ -182,7 +212,7 @@ private static CliCommandHelpHandler CreateHandler(
Out = new AnsiConsoleOutput(writer)
});
- var settings = new HelpHandlerSettings { CommandKind = commandKind };
+ var settings = new HelpHandlerSettings { CommandKinds = commandKinds };
optionsHelpGenerator = A.Fake();
var store = commandStore ?? new CliCommandStore();
diff --git a/tests/CreativeCoders.Cli.Tests/Hosting/DefaultCliHostBuilderTests.cs b/tests/CreativeCoders.Cli.Tests/Hosting/DefaultCliHostBuilderTests.cs
index 53d4c75b..37196103 100644
--- a/tests/CreativeCoders.Cli.Tests/Hosting/DefaultCliHostBuilderTests.cs
+++ b/tests/CreativeCoders.Cli.Tests/Hosting/DefaultCliHostBuilderTests.cs
@@ -51,9 +51,9 @@ public void Build_WithHelpEnabled_RegistersHelpHandlerAndSettings()
services
.GetRequiredService()
- .CommandKind
+ .CommandKinds
.Should()
- .Be(HelpCommandKind.Command);
+ .BeEquivalentTo([HelpCommandKind.Command]);
}
[Fact]
diff --git a/tests/CreativeCoders.Cli.Tests/Hosting/DefaultCliHostTests.cs b/tests/CreativeCoders.Cli.Tests/Hosting/DefaultCliHostTests.cs
index 288c9c2c..95f859ae 100644
--- a/tests/CreativeCoders.Cli.Tests/Hosting/DefaultCliHostTests.cs
+++ b/tests/CreativeCoders.Cli.Tests/Hosting/DefaultCliHostTests.cs
@@ -31,7 +31,7 @@ public async Task RunAsync_WhenHelpIsRequested_PrintsHelpAndReturnsSuccess()
A.CallTo(() => helpHandler.ShouldPrintHelp(args))
.Returns(true);
- var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler);
+ var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler, [], []);
// Act
var result = await host.RunAsync(args);
@@ -45,6 +45,76 @@ public async Task RunAsync_WhenHelpIsRequested_PrintsHelpAndReturnsSuccess()
.Be(CliExitCodes.Success);
}
+ [Fact]
+ public async Task RunAsync_WhenHelpIsRequested_ExecutesHelpPreAndPostProcessors()
+ {
+ // Arrange
+ var args = new[] { "help" };
+
+ var ansiConsole = A.Fake();
+ var commandStore = A.Fake();
+ var serviceProvider = A.Fake();
+ var helpHandler = A.Fake();
+
+ var preHelpProcessor = A.Fake();
+ var preAlwaysProcessor = A.Fake();
+ var preCommandProcessor = A.Fake();
+ var postHelpProcessor = A.Fake();
+ var postAlwaysProcessor = A.Fake();
+ var postCommandProcessor = A.Fake();
+
+ SetupServiceProvider(serviceProvider, null);
+
+ A.CallTo(() => helpHandler.ShouldPrintHelp(args))
+ .Returns(true);
+
+ A.CallTo(() => preHelpProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.OnlyOnHelp);
+ A.CallTo(() => preAlwaysProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.Always);
+ A.CallTo(() => preCommandProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.OnlyOnCommand);
+
+ A.CallTo(() => postHelpProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.OnlyOnHelp);
+ A.CallTo(() => postAlwaysProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.Always);
+ A.CallTo(() => postCommandProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.OnlyOnCommand);
+
+ var host = new DefaultCliHost(
+ ansiConsole,
+ commandStore,
+ serviceProvider,
+ helpHandler,
+ [preHelpProcessor, preAlwaysProcessor, preCommandProcessor],
+ [postHelpProcessor, postAlwaysProcessor, postCommandProcessor]);
+
+ // Act
+ var result = await host.RunAsync(args);
+
+ // Assert
+ A.CallTo(() => preHelpProcessor.ExecuteAsync(args))
+ .MustHaveHappenedOnceExactly();
+ A.CallTo(() => preAlwaysProcessor.ExecuteAsync(args))
+ .MustHaveHappenedOnceExactly();
+ A.CallTo(() => preCommandProcessor.ExecuteAsync(args))
+ .MustNotHaveHappened();
+
+ A.CallTo(() => postHelpProcessor.ExecuteAsync(A.That.Matches(r =>
+ r.ExitCode == CliExitCodes.Success)))
+ .MustHaveHappenedOnceExactly();
+ A.CallTo(() => postAlwaysProcessor.ExecuteAsync(A.That.Matches(r =>
+ r.ExitCode == CliExitCodes.Success)))
+ .MustHaveHappenedOnceExactly();
+ A.CallTo(() => postCommandProcessor.ExecuteAsync(A.Ignored))
+ .MustNotHaveHappened();
+
+ result.ExitCode
+ .Should()
+ .Be(CliExitCodes.Success);
+ }
+
[Fact]
public async Task RunAsync_WhenCommandNotFound_PrintsSuggestionsAndReturnsNotFound()
{
@@ -69,7 +139,7 @@ public async Task RunAsync_WhenCommandNotFound_PrintsSuggestionsAndReturnsNotFou
A.CallTo(() => commandStore.FindCommandGroupNode(args))
.Returns(new FindCommandNodeResult(groupNode, []));
- var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler);
+ var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler, [], []);
// Act
var result = await host.RunAsync(args);
@@ -83,6 +153,214 @@ public async Task RunAsync_WhenCommandNotFound_PrintsSuggestionsAndReturnsNotFou
.MustHaveHappenedOnceExactly();
}
+ [Fact]
+ public async Task RunAsync_WhenCommandIsExecuted_ExecutesCommandPreAndPostProcessors()
+ {
+ // Arrange
+ var args = new[] { "run" };
+
+ var ansiConsole = A.Fake();
+ var commandStore = A.Fake();
+ var serviceProvider = A.Fake();
+ var helpHandler = A.Fake();
+
+ var preHelpProcessor = A.Fake();
+ var preAlwaysProcessor = A.Fake();
+ var preCommandProcessor = A.Fake();
+ var postHelpProcessor = A.Fake();
+ var postAlwaysProcessor = A.Fake();
+ var postCommandProcessor = A.Fake();
+
+ SetupServiceProvider(serviceProvider, new CliCommandContext());
+
+ A.CallTo(() => helpHandler.ShouldPrintHelp(args))
+ .Returns(false);
+
+ A.CallTo(() => preHelpProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.OnlyOnHelp);
+ A.CallTo(() => preAlwaysProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.Always);
+ A.CallTo(() => preCommandProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.OnlyOnCommand);
+
+ A.CallTo(() => postHelpProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.OnlyOnHelp);
+ A.CallTo(() => postAlwaysProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.Always);
+ A.CallTo(() => postCommandProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.OnlyOnCommand);
+
+ var commandInfo = new CliCommandInfo
+ {
+ CommandAttribute = new CliCommandAttribute(["run"]),
+ CommandType = typeof(DummyCommandWithResult)
+ };
+
+ var commandNode = new CliCommandNode(commandInfo, "run", null);
+
+ A.CallTo(() => commandStore.FindCommandNode(args))
+ .Returns(new FindCommandNodeResult(commandNode, []));
+
+ A.CallTo(() => serviceProvider.GetService(typeof(int)))
+ .Returns(17);
+
+ var host = new DefaultCliHost(
+ ansiConsole,
+ commandStore,
+ serviceProvider,
+ helpHandler,
+ [preHelpProcessor, preAlwaysProcessor, preCommandProcessor],
+ [postHelpProcessor, postAlwaysProcessor, postCommandProcessor]);
+
+ // Act
+ var result = await host.RunAsync(args);
+
+ // Assert
+ A.CallTo(() => preCommandProcessor.ExecuteAsync(args))
+ .MustHaveHappenedOnceExactly();
+ A.CallTo(() => preAlwaysProcessor.ExecuteAsync(args))
+ .MustHaveHappenedOnceExactly();
+ A.CallTo(() => preHelpProcessor.ExecuteAsync(args))
+ .MustNotHaveHappened();
+
+ A.CallTo(() => postCommandProcessor.ExecuteAsync(A.That.Matches(r =>
+ r.ExitCode == 17)))
+ .MustHaveHappenedOnceExactly();
+ A.CallTo(() => postAlwaysProcessor.ExecuteAsync(A.That.Matches(r =>
+ r.ExitCode == 17)))
+ .MustHaveHappenedOnceExactly();
+ A.CallTo(() => postHelpProcessor.ExecuteAsync(A.Ignored))
+ .MustNotHaveHappened();
+
+ result.ExitCode
+ .Should()
+ .Be(17);
+ }
+
+ [Fact]
+ public async Task RunAsync_PreProcessorThrowsException_ExceptionIsCatchedAndExitCodeSet()
+ {
+ // Arrange
+ var args = new[] { "run" };
+
+ var ansiConsole = A.Fake();
+ var commandStore = A.Fake();
+ var serviceProvider = A.Fake();
+ var helpHandler = A.Fake();
+
+ var preProcessor = A.Fake();
+ var secondPreProcessor = A.Fake();
+
+ SetupServiceProvider(serviceProvider, new CliCommandContext());
+
+ A.CallTo(() => helpHandler.ShouldPrintHelp(args))
+ .Returns(false);
+
+ A.CallTo(() => preProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.Always);
+
+ A.CallTo(() => secondPreProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.Always);
+
+ A.CallTo(() => preProcessor.ExecuteAsync(args))
+ .Throws(new Exception("Test exception"));
+
+ var commandInfo = new CliCommandInfo
+ {
+ CommandAttribute = new CliCommandAttribute(["run"]),
+ CommandType = typeof(DummyCommandWithResult)
+ };
+
+ var commandNode = new CliCommandNode(commandInfo, "run", null);
+
+ A.CallTo(() => commandStore.FindCommandNode(args))
+ .Returns(new FindCommandNodeResult(commandNode, []));
+
+ A.CallTo(() => serviceProvider.GetService(typeof(int)))
+ .Returns(17);
+
+ var host = new DefaultCliHost(
+ ansiConsole,
+ commandStore,
+ serviceProvider,
+ helpHandler,
+ [preProcessor, secondPreProcessor],
+ []);
+
+ // Act
+ var result = await host.RunAsync(args);
+
+ // Assert
+ A.CallTo(() => secondPreProcessor.ExecuteAsync(args))
+ .MustNotHaveHappened();
+
+ result.ExitCode
+ .Should()
+ .Be(CliExitCodes.PreProcessorFailed);
+ }
+
+ [Fact]
+ public async Task RunAsync_PostProcessorThrowsException_ExceptionIsCatchedAndExitCodeSet()
+ {
+ // Arrange
+ var args = new[] { "run" };
+
+ var ansiConsole = A.Fake();
+ var commandStore = A.Fake();
+ var serviceProvider = A.Fake();
+ var helpHandler = A.Fake();
+
+ var postProcessor = A.Fake();
+ var secondPostProcessor = A.Fake();
+
+ SetupServiceProvider(serviceProvider, new CliCommandContext());
+
+ A.CallTo(() => helpHandler.ShouldPrintHelp(args))
+ .Returns(false);
+
+ A.CallTo(() => postProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.Always);
+
+ A.CallTo(() => secondPostProcessor.ExecutionCondition)
+ .Returns(CliProcessorExecutionCondition.Always);
+
+ A.CallTo(() => postProcessor.ExecuteAsync(A.Ignored))
+ .Throws(new Exception("Test exception"));
+
+ var commandInfo = new CliCommandInfo
+ {
+ CommandAttribute = new CliCommandAttribute(["run"]),
+ CommandType = typeof(DummyCommandWithResult)
+ };
+
+ var commandNode = new CliCommandNode(commandInfo, "run", null);
+
+ A.CallTo(() => commandStore.FindCommandNode(args))
+ .Returns(new FindCommandNodeResult(commandNode, []));
+
+ A.CallTo(() => serviceProvider.GetService(typeof(int)))
+ .Returns(17);
+
+ var host = new DefaultCliHost(
+ ansiConsole,
+ commandStore,
+ serviceProvider,
+ helpHandler,
+ [],
+ [postProcessor, secondPostProcessor]);
+
+ // Act
+ var result = await host.RunAsync(args);
+
+ // Assert
+ A.CallTo(() => secondPostProcessor.ExecuteAsync(A.Ignored))
+ .MustNotHaveHappened();
+
+ result.ExitCode
+ .Should()
+ .Be(CliExitCodes.PostProcessorFailed);
+ }
+
[Fact]
public async Task RunAsync_WhenCommandWithoutOptions_ExecutesAndReturnsResult()
{
@@ -113,7 +391,7 @@ public async Task RunAsync_WhenCommandWithoutOptions_ExecutesAndReturnsResult()
A.CallTo(() => serviceProvider.GetService(typeof(int)))
.Returns(5);
- var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler);
+ var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler, [], []);
// Act
var result = await host.RunAsync(args);
@@ -161,7 +439,7 @@ public async Task RunAsync_WhenCommandWithAbortException_ExecutesAndReturnsExcep
A.CallTo(() => commandStore.FindCommandNode(args))
.Returns(new FindCommandNodeResult(commandNode, []));
- var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler);
+ var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler, [], []);
// Act
var result = await host.RunAsync(args);
@@ -223,7 +501,7 @@ public async Task RunMainAsync_WhenCommandWithoutOptions_ExecutesAndReturnsIntRe
A.CallTo(() => serviceProvider.GetService(typeof(int)))
.Returns(5);
- var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler);
+ var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler, [], []);
// Act
var result = await host.RunMainAsync(args);
@@ -265,7 +543,7 @@ public async Task RunAsync_OnlyArgsForCommand_CommandContextHasCorrectArgs()
A.CallTo(() => serviceProvider.GetService(typeof(int)))
.Returns(5);
- var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler);
+ var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler, [], []);
// Act
var result = await host.RunAsync(args);
@@ -315,7 +593,7 @@ public async Task RunAsync_ArgsForCommandAndOptions_CommandContextHasCorrectArgs
A.CallTo(() => serviceProvider.GetService(typeof(int)))
.Returns(5);
- var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler);
+ var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler, [], []);
// Act
var result = await host.RunAsync(args);
@@ -361,7 +639,7 @@ public async Task RunAsync_WhenCommandCreationFails_PrintsErrorAndReturnsExitCod
A.CallTo(() => commandStore.FindCommandNode(args))
.Returns(new FindCommandNodeResult(commandNode, []));
- var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler);
+ var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler, [], []);
// Act
var result = await host.RunAsync(args);
@@ -401,7 +679,7 @@ public async Task RunAsync_WithOptionsValidation_ExecutesAndReturnsResult()
A.CallTo(() => commandStore.FindCommandNode(args))
.Returns(new FindCommandNodeResult(commandNode, []));
- var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler);
+ var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler, [], []);
// Act
var result = await host.RunAsync(args);
@@ -441,7 +719,7 @@ public async Task RunAsync_WithOptionsValidationActivatedButWithoutValidation_Ex
A.CallTo(() => commandStore.FindCommandNode(args))
.Returns(new FindCommandNodeResult(commandNode, []));
- var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler);
+ var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler, [], []);
// Act
var result = await host.RunAsync(args);
@@ -481,7 +759,7 @@ public async Task RunAsync_WithOptionsValidationIsInvalid_ExecutesAndReturnsInva
A.CallTo(() => commandStore.FindCommandNode(args))
.Returns(new FindCommandNodeResult(commandNode, []));
- var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler);
+ var host = new DefaultCliHost(ansiConsole, commandStore, serviceProvider, helpHandler, [], []);
// Act
var result = await host.RunAsync(args);