mirror of
https://github.com/spectreconsole/spectre.console.git
synced 2025-10-25 15:19:23 +00:00
Add top-level CancellationToken support to Spectre.Console.Cli
Also raise CA2016 (forward the CancellationToken parameter to methods that take one) to warning Fixes #701
This commit is contained in:
committed by
Patrik Svensson
parent
d90e94dbb3
commit
f5f61ca610
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Generator.Models;
|
||||
using Scriban;
|
||||
using Spectre.Console.Cli;
|
||||
@@ -21,7 +22,7 @@ namespace Generator.Commands
|
||||
public string Input { get; set; }
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, Settings settings)
|
||||
public override int Execute(CommandContext context, Settings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
var templates = new FilePath[]
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Html.Parser;
|
||||
using Generator.Models;
|
||||
@@ -39,7 +40,7 @@ namespace Generator.Commands
|
||||
_parser = new HtmlParser();
|
||||
}
|
||||
|
||||
public override async Task<int> ExecuteAsync(CommandContext context, Settings settings)
|
||||
public override async Task<int> ExecuteAsync(CommandContext context, Settings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
var output = new DirectoryPath(settings.Output);
|
||||
if (!_fileSystem.Directory.Exists(settings.Output))
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Generator.Commands.Samples;
|
||||
using Spectre.Console;
|
||||
using Spectre.Console.Cli;
|
||||
@@ -38,7 +39,7 @@ namespace Generator.Commands
|
||||
_console = new AsciiCastConsole(console);
|
||||
}
|
||||
|
||||
public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings)
|
||||
public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
var samples = typeof(BaseSample).Assembly
|
||||
.GetTypes()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Generator.Models;
|
||||
using Scriban;
|
||||
using Spectre.Console.Cli;
|
||||
@@ -16,7 +17,7 @@ namespace Generator.Commands
|
||||
_fileSystem = new FileSystem();
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, GeneratorSettings settings)
|
||||
public override int Execute(CommandContext context, GeneratorSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
// Read the spinner model.
|
||||
var spinners = new List<Spinner>();
|
||||
|
||||
@@ -100,5 +100,8 @@ dotnet_diagnostic.RCS1047.severity = none
|
||||
# RCS1090: Call 'ConfigureAwait(false)'.
|
||||
dotnet_diagnostic.RCS1090.severity = warning
|
||||
|
||||
# The file header is missing or not located at the top of the file
|
||||
dotnet_diagnostic.SA1633.severity = none
|
||||
# SA1633: The file header is missing or not located at the top of the file
|
||||
dotnet_diagnostic.SA1633.severity = none
|
||||
|
||||
# CA2016: Forward the CancellationToken parameter to methods that take one
|
||||
dotnet_diagnostic.CA2016.severity = warning
|
||||
@@ -9,19 +9,20 @@ public abstract class AsyncCommand : ICommand<EmptyCommandSettings>
|
||||
/// Executes the command.
|
||||
/// </summary>
|
||||
/// <param name="context">The command context.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param>
|
||||
/// <returns>An integer indicating whether or not the command executed successfully.</returns>
|
||||
public abstract Task<int> ExecuteAsync(CommandContext context);
|
||||
public abstract Task<int> ExecuteAsync(CommandContext context, CancellationToken cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
Task<int> ICommand<EmptyCommandSettings>.Execute(CommandContext context, EmptyCommandSettings settings)
|
||||
Task<int> ICommand<EmptyCommandSettings>.ExecuteAsync(CommandContext context, EmptyCommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return ExecuteAsync(context);
|
||||
return ExecuteAsync(context, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
Task<int> ICommand.Execute(CommandContext context, CommandSettings settings)
|
||||
Task<int> ICommand.ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return ExecuteAsync(context);
|
||||
return ExecuteAsync(context, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -23,8 +23,9 @@ public abstract class AsyncCommand<TSettings> : ICommand<TSettings>
|
||||
/// </summary>
|
||||
/// <param name="context">The command context.</param>
|
||||
/// <param name="settings">The settings.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param>
|
||||
/// <returns>An integer indicating whether or not the command executed successfully.</returns>
|
||||
public abstract Task<int> ExecuteAsync(CommandContext context, TSettings settings);
|
||||
public abstract Task<int> ExecuteAsync(CommandContext context, TSettings settings, CancellationToken cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings)
|
||||
@@ -33,15 +34,15 @@ public abstract class AsyncCommand<TSettings> : ICommand<TSettings>
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
Task<int> ICommand.Execute(CommandContext context, CommandSettings settings)
|
||||
Task<int> ICommand.ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
Debug.Assert(settings is TSettings, "Command settings is of unexpected type.");
|
||||
return ExecuteAsync(context, (TSettings)settings);
|
||||
return ExecuteAsync(context, (TSettings)settings, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
Task<int> ICommand<TSettings>.Execute(CommandContext context, TSettings settings)
|
||||
Task<int> ICommand<TSettings>.ExecuteAsync(CommandContext context, TSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return ExecuteAsync(context, settings);
|
||||
return ExecuteAsync(context, settings, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -10,19 +10,20 @@ public abstract class Command : ICommand<EmptyCommandSettings>
|
||||
/// Executes the command.
|
||||
/// </summary>
|
||||
/// <param name="context">The command context.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param>
|
||||
/// <returns>An integer indicating whether or not the command executed successfully.</returns>
|
||||
public abstract int Execute(CommandContext context);
|
||||
public abstract int Execute(CommandContext context, CancellationToken cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
Task<int> ICommand<EmptyCommandSettings>.Execute(CommandContext context, EmptyCommandSettings settings)
|
||||
Task<int> ICommand<EmptyCommandSettings>.ExecuteAsync(CommandContext context, EmptyCommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(Execute(context));
|
||||
return Task.FromResult(Execute(context, cancellationToken));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
Task<int> ICommand.Execute(CommandContext context, CommandSettings settings)
|
||||
Task<int> ICommand.ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(Execute(context));
|
||||
return Task.FromResult(Execute(context, cancellationToken));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -26,10 +26,7 @@ public sealed class CommandApp : ICommandApp
|
||||
_executor = new CommandExecutor(registrar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the command line application.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
/// <inheritdoc/>
|
||||
public void Configure(Action<IConfigurator> configuration)
|
||||
{
|
||||
if (configuration == null)
|
||||
@@ -51,22 +48,14 @@ public sealed class CommandApp : ICommandApp
|
||||
return new DefaultCommandConfigurator(GetConfigurator().SetDefaultCommand<TCommand>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the command line application with specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <returns>The exit code from the executed command.</returns>
|
||||
public int Run(IEnumerable<string> args)
|
||||
/// <inheritdoc/>
|
||||
public int Run(IEnumerable<string> args, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return RunAsync(args).GetAwaiter().GetResult();
|
||||
return RunAsync(args, cancellationToken).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the command line application with specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <returns>The exit code from the executed command.</returns>
|
||||
public async Task<int> RunAsync(IEnumerable<string> args)
|
||||
/// <inheritdoc/>
|
||||
public async Task<int> RunAsync(IEnumerable<string> args, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -86,7 +75,7 @@ public sealed class CommandApp : ICommandApp
|
||||
}
|
||||
|
||||
return await _executor
|
||||
.Execute(_configurator, args)
|
||||
.ExecuteAsync(_configurator, args, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -109,6 +98,11 @@ public sealed class CommandApp : ICommandApp
|
||||
return _configurator.Settings.ExceptionHandler(ex, null);
|
||||
}
|
||||
|
||||
if (ex is OperationCanceledException)
|
||||
{
|
||||
return _configurator.Settings.CancellationExitCode;
|
||||
}
|
||||
|
||||
// Render the exception.
|
||||
var pretty = GetRenderableErrorMessage(ex);
|
||||
if (pretty != null)
|
||||
|
||||
@@ -25,33 +25,22 @@ public sealed class CommandApp<TDefaultCommand> : ICommandApp
|
||||
_defaultCommandConfigurator = _app.SetDefaultCommand<TDefaultCommand>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the command line application.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
/// <inheritdoc/>
|
||||
public void Configure(Action<IConfigurator> configuration)
|
||||
{
|
||||
_app.Configure(configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the command line application with specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <returns>The exit code from the executed command.</returns>
|
||||
public int Run(IEnumerable<string> args)
|
||||
/// <inheritdoc/>
|
||||
public int Run(IEnumerable<string> args, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _app.Run(args);
|
||||
return _app.Run(args, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the command line application with specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <returns>The exit code from the executed command.</returns>
|
||||
public Task<int> RunAsync(IEnumerable<string> args)
|
||||
/// <inheritdoc/>
|
||||
public Task<int> RunAsync(IEnumerable<string> args, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return _app.RunAsync(args);
|
||||
return _app.RunAsync(args, cancellationToken);
|
||||
}
|
||||
|
||||
internal Configurator GetConfigurator()
|
||||
|
||||
@@ -24,8 +24,9 @@ public abstract class Command<TSettings> : ICommand<TSettings>
|
||||
/// </summary>
|
||||
/// <param name="context">The command context.</param>
|
||||
/// <param name="settings">The settings.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param>
|
||||
/// <returns>An integer indicating whether or not the command executed successfully.</returns>
|
||||
public abstract int Execute(CommandContext context, TSettings settings);
|
||||
public abstract int Execute(CommandContext context, TSettings settings, CancellationToken cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings)
|
||||
@@ -34,15 +35,15 @@ public abstract class Command<TSettings> : ICommand<TSettings>
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
Task<int> ICommand.Execute(CommandContext context, CommandSettings settings)
|
||||
Task<int> ICommand.ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
Debug.Assert(settings is TSettings, "Command settings is of unexpected type.");
|
||||
return Task.FromResult(Execute(context, (TSettings)settings));
|
||||
return Task.FromResult(Execute(context, (TSettings)settings, cancellationToken));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
Task<int> ICommand<TSettings>.Execute(CommandContext context, TSettings settings)
|
||||
Task<int> ICommand<TSettings>.ExecuteAsync(CommandContext context, TSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(Execute(context, settings));
|
||||
return Task.FromResult(Execute(context, settings, cancellationToken));
|
||||
}
|
||||
}
|
||||
@@ -201,6 +201,24 @@ public static class ConfiguratorExtensions
|
||||
return configurator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tells the command line application to return the specified exit code when it's aborted through the <see cref="CancellationToken"/>.
|
||||
/// The default cancellation exit code is 130.
|
||||
/// </summary>
|
||||
/// <param name="configurator">The configurator.</param>
|
||||
/// <param name="exitCode">The exit code to return in case of cancellation.</param>
|
||||
/// <returns>A configurator that can be used to configure the application further.</returns>
|
||||
public static IConfigurator CancellationExitCode(this IConfigurator configurator, int exitCode)
|
||||
{
|
||||
if (configurator == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurator));
|
||||
}
|
||||
|
||||
configurator.Settings.CancellationExitCode = exitCode;
|
||||
return configurator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures case sensitivity.
|
||||
/// </summary>
|
||||
@@ -304,14 +322,14 @@ public static class ConfiguratorExtensions
|
||||
public static ICommandConfigurator AddDelegate(
|
||||
this IConfigurator configurator,
|
||||
string name,
|
||||
Func<CommandContext, int> func)
|
||||
Func<CommandContext, CancellationToken, int> func)
|
||||
{
|
||||
if (configurator == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurator));
|
||||
}
|
||||
|
||||
return configurator.AddDelegate<EmptyCommandSettings>(name, (c, _) => func(c));
|
||||
return configurator.AddDelegate<EmptyCommandSettings>(name, (c, _, ct) => func(c, ct));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -324,14 +342,14 @@ public static class ConfiguratorExtensions
|
||||
public static ICommandConfigurator AddAsyncDelegate(
|
||||
this IConfigurator configurator,
|
||||
string name,
|
||||
Func<CommandContext, Task<int>> func)
|
||||
Func<CommandContext, CancellationToken, Task<int>> func)
|
||||
{
|
||||
if (configurator == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurator));
|
||||
}
|
||||
|
||||
return configurator.AddAsyncDelegate<EmptyCommandSettings>(name, (c, _) => func(c));
|
||||
return configurator.AddAsyncDelegate<EmptyCommandSettings>(name, (c, _, ct) => func(c, ct));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -345,7 +363,7 @@ public static class ConfiguratorExtensions
|
||||
public static ICommandConfigurator AddDelegate<TSettings>(
|
||||
this IConfigurator<TSettings>? configurator,
|
||||
string name,
|
||||
Func<CommandContext, int> func)
|
||||
Func<CommandContext, CancellationToken, int> func)
|
||||
where TSettings : CommandSettings
|
||||
{
|
||||
if (typeof(TSettings).IsAbstract)
|
||||
@@ -358,7 +376,7 @@ public static class ConfiguratorExtensions
|
||||
throw new ArgumentNullException(nameof(configurator));
|
||||
}
|
||||
|
||||
return configurator.AddDelegate<TSettings>(name, (c, _) => func(c));
|
||||
return configurator.AddDelegate<TSettings>(name, (c, _, ct) => func(c, ct));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -372,7 +390,7 @@ public static class ConfiguratorExtensions
|
||||
public static ICommandConfigurator AddAsyncDelegate<TSettings>(
|
||||
this IConfigurator<TSettings> configurator,
|
||||
string name,
|
||||
Func<CommandContext, Task<int>> func)
|
||||
Func<CommandContext, CancellationToken, Task<int>> func)
|
||||
where TSettings : CommandSettings
|
||||
{
|
||||
if (configurator == null)
|
||||
@@ -380,7 +398,7 @@ public static class ConfiguratorExtensions
|
||||
throw new ArgumentNullException(nameof(configurator));
|
||||
}
|
||||
|
||||
return configurator.AddAsyncDelegate<TSettings>(name, (c, _) => func(c));
|
||||
return configurator.AddAsyncDelegate<TSettings>(name, (c, _, ct) => func(c, ct));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -18,6 +18,7 @@ public interface ICommand
|
||||
/// </summary>
|
||||
/// <param name="context">The command context.</param>
|
||||
/// <param name="settings">The settings.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param>
|
||||
/// <returns>The validation result.</returns>
|
||||
Task<int> Execute(CommandContext context, CommandSettings settings);
|
||||
Task<int> ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -15,13 +15,15 @@ public interface ICommandApp
|
||||
/// Runs the command line application with specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the application.</param>
|
||||
/// <returns>The exit code from the executed command.</returns>
|
||||
int Run(IEnumerable<string> args);
|
||||
int Run(IEnumerable<string> args, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Runs the command line application with specified arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the application.</param>
|
||||
/// <returns>The exit code from the executed command.</returns>
|
||||
Task<int> RunAsync(IEnumerable<string> args);
|
||||
Task<int> RunAsync(IEnumerable<string> args, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -88,6 +88,12 @@ public interface ICommandAppSettings
|
||||
/// </summary>
|
||||
bool PropagateExceptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value used as the application exit code when it's aborted through the <see cref="CancellationToken"/>.
|
||||
/// The default cancellation exit code is 130.
|
||||
/// </summary>
|
||||
int CancellationExitCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not examples should be validated.
|
||||
/// </summary>
|
||||
|
||||
@@ -12,6 +12,7 @@ public interface ICommand<TSettings> : ICommandLimiter<TSettings>
|
||||
/// </summary>
|
||||
/// <param name="context">The command context.</param>
|
||||
/// <param name="settings">The settings.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to abort the command.</param>
|
||||
/// <returns>An integer indicating whether or not the command executed successfully.</returns>
|
||||
Task<int> Execute(CommandContext context, TSettings settings);
|
||||
Task<int> ExecuteAsync(CommandContext context, TSettings settings, CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public interface IConfigurator
|
||||
/// <param name="name">The name of the command.</param>
|
||||
/// <param name="func">The delegate to execute as part of command execution.</param>
|
||||
/// <returns>A command configurator that can be used to configure the command further.</returns>
|
||||
ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, int> func)
|
||||
ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, CancellationToken, int> func)
|
||||
where TSettings : CommandSettings;
|
||||
|
||||
/// <summary>
|
||||
@@ -58,7 +58,7 @@ public interface IConfigurator
|
||||
/// <param name="name">The name of the command.</param>
|
||||
/// <param name="func">The delegate to execute as part of command execution.</param>
|
||||
/// <returns>A command configurator that can be used to configure the command further.</returns>
|
||||
ICommandConfigurator AddAsyncDelegate<TSettings>(string name, Func<CommandContext, TSettings, Task<int>> func)
|
||||
ICommandConfigurator AddAsyncDelegate<TSettings>(string name, Func<CommandContext, TSettings, CancellationToken, Task<int>> func)
|
||||
where TSettings : CommandSettings;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -54,7 +54,7 @@ public interface IConfigurator<in TSettings>
|
||||
/// <param name="name">The name of the command.</param>
|
||||
/// <param name="func">The delegate to execute as part of command execution.</param>
|
||||
/// <returns>A command configurator that can be used to configure the command further.</returns>
|
||||
ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, int> func)
|
||||
ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, CancellationToken, int> func)
|
||||
where TDerivedSettings : TSettings;
|
||||
|
||||
/// <summary>
|
||||
@@ -64,7 +64,7 @@ public interface IConfigurator<in TSettings>
|
||||
/// <param name="name">The name of the command.</param>
|
||||
/// <param name="func">The delegate to execute as part of command execution.</param>
|
||||
/// <returns>A command configurator that can be used to configure the command further.</returns>
|
||||
ICommandConfigurator AddAsyncDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, Task<int>> func)
|
||||
ICommandConfigurator AddAsyncDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, CancellationToken, Task<int>> func)
|
||||
where TDerivedSettings : TSettings;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -12,7 +12,7 @@ internal sealed class CommandExecutor
|
||||
_registrar.Register(typeof(DefaultPairDeconstructor), typeof(DefaultPairDeconstructor));
|
||||
}
|
||||
|
||||
public async Task<int> Execute(IConfiguration configuration, IEnumerable<string> args)
|
||||
public async Task<int> ExecuteAsync(IConfiguration configuration, IEnumerable<string> args, CancellationToken cancellationToken)
|
||||
{
|
||||
CommandTreeParserResult parsedResult;
|
||||
|
||||
@@ -125,7 +125,7 @@ internal sealed class CommandExecutor
|
||||
leaf.Command.Data);
|
||||
|
||||
// Execute the command tree.
|
||||
return await Execute(leaf, parsedResult.Tree, context, resolver, configuration).ConfigureAwait(false);
|
||||
return await ExecuteAsync(leaf, parsedResult.Tree, context, resolver, configuration, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,12 +222,13 @@ internal sealed class CommandExecutor
|
||||
return (parsedResult, tokenizerResult);
|
||||
}
|
||||
|
||||
private static async Task<int> Execute(
|
||||
private static async Task<int> ExecuteAsync(
|
||||
CommandTree leaf,
|
||||
CommandTree tree,
|
||||
CommandContext context,
|
||||
ITypeResolver resolver,
|
||||
IConfiguration configuration)
|
||||
IConfiguration configuration,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -256,7 +257,7 @@ internal sealed class CommandExecutor
|
||||
}
|
||||
|
||||
// Execute the command.
|
||||
var result = await command.Execute(context, settings);
|
||||
var result = await command.ExecuteAsync(context, settings, cancellationToken);
|
||||
foreach (var interceptor in interceptors)
|
||||
{
|
||||
interceptor.InterceptResult(context, settings, ref result);
|
||||
|
||||
@@ -27,7 +27,7 @@ internal sealed class ExplainCommand : Command<ExplainCommand.Settings>, IBuiltI
|
||||
public bool IncludeHidden { get; set; }
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, Settings settings)
|
||||
public override int Execute(CommandContext context, Settings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
var tree = new Tree("CLI Configuration");
|
||||
tree.AddNode(ValueMarkup("Application Name", _commandModel.ApplicationName, "no application name"));
|
||||
|
||||
@@ -13,7 +13,7 @@ internal sealed class OpenCliGeneratorCommand : Command, IBuiltInCommand
|
||||
_model = model ?? throw new ArgumentNullException(nameof(model));
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context)
|
||||
public override int Execute(CommandContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
var document = new OpenCliDocument
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ internal sealed class VersionCommand : Command, IBuiltInCommand
|
||||
_writer = configuration.Settings.Console.GetConsole();
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context)
|
||||
public override int Execute(CommandContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
_writer.MarkupLine(
|
||||
"[yellow]Spectre.Cli[/] version [aqua]{0}[/]",
|
||||
|
||||
@@ -13,7 +13,7 @@ internal sealed class XmlDocCommand : Command, IBuiltInCommand
|
||||
_writer = configuration.Settings.Console.GetConsole();
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context)
|
||||
public override int Execute(CommandContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
_writer.Write(Serialize(_model), Style.Plain);
|
||||
return 0;
|
||||
|
||||
@@ -18,6 +18,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings
|
||||
public HelpProviderStyle? HelpProviderStyles { get; set; }
|
||||
public bool StrictParsing { get; set; }
|
||||
public bool ConvertFlagsToRemainingArguments { get; set; }
|
||||
public int CancellationExitCode { get; set; }
|
||||
|
||||
public ParsingMode ParsingMode =>
|
||||
StrictParsing ? ParsingMode.Strict : ParsingMode.Relaxed;
|
||||
@@ -33,6 +34,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings
|
||||
TrimTrailingPeriod = true;
|
||||
HelpProviderStyles = HelpProviderStyle.Default;
|
||||
ConvertFlagsToRemainingArguments = false;
|
||||
CancellationExitCode = 130;
|
||||
}
|
||||
|
||||
public bool IsTrue(Func<CommandAppSettings, bool> func, string environmentVariableName)
|
||||
|
||||
@@ -56,19 +56,19 @@ internal sealed class Configurator : IUnsafeConfigurator, IConfigurator, IConfig
|
||||
return new CommandConfigurator(command);
|
||||
}
|
||||
|
||||
public ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, int> func)
|
||||
public ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, CancellationToken, int> func)
|
||||
where TSettings : CommandSettings
|
||||
{
|
||||
var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>(
|
||||
name, (context, settings) => Task.FromResult(func(context, (TSettings)settings))));
|
||||
name, (context, settings, cancellationToken) => Task.FromResult(func(context, (TSettings)settings, cancellationToken))));
|
||||
return new CommandConfigurator(command);
|
||||
}
|
||||
|
||||
public ICommandConfigurator AddAsyncDelegate<TSettings>(string name, Func<CommandContext, TSettings, Task<int>> func)
|
||||
public ICommandConfigurator AddAsyncDelegate<TSettings>(string name, Func<CommandContext, TSettings, CancellationToken, Task<int>> func)
|
||||
where TSettings : CommandSettings
|
||||
{
|
||||
var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>(
|
||||
name, (context, settings) => func(context, (TSettings)settings)));
|
||||
name, (context, settings, cancellationToken) => func(context, (TSettings)settings, cancellationToken)));
|
||||
return new CommandConfigurator(command);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,21 +46,21 @@ internal sealed class Configurator<TSettings> : IUnsafeBranchConfigurator, IConf
|
||||
return configurator;
|
||||
}
|
||||
|
||||
public ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, int> func)
|
||||
public ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, CancellationToken, int> func)
|
||||
where TDerivedSettings : TSettings
|
||||
{
|
||||
var command = ConfiguredCommand.FromDelegate<TDerivedSettings>(
|
||||
name, (context, settings) => Task.FromResult(func(context, (TDerivedSettings)settings)));
|
||||
name, (context, settings, cancellationToken) => Task.FromResult(func(context, (TDerivedSettings)settings, cancellationToken)));
|
||||
|
||||
_command.Children.Add(command);
|
||||
return new CommandConfigurator(command);
|
||||
}
|
||||
|
||||
public ICommandConfigurator AddAsyncDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, Task<int>> func)
|
||||
public ICommandConfigurator AddAsyncDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, CancellationToken, Task<int>> func)
|
||||
where TDerivedSettings : TSettings
|
||||
{
|
||||
var command = ConfiguredCommand.FromDelegate<TDerivedSettings>(
|
||||
name, (context, settings) => func(context, (TDerivedSettings)settings));
|
||||
name, (context, settings, cancellationToken) => func(context, (TDerivedSettings)settings, cancellationToken));
|
||||
|
||||
_command.Children.Add(command);
|
||||
return new CommandConfigurator(command);
|
||||
|
||||
@@ -8,7 +8,7 @@ internal sealed class ConfiguredCommand
|
||||
public object? Data { get; set; }
|
||||
public Type? CommandType { get; }
|
||||
public Type SettingsType { get; }
|
||||
public Func<CommandContext, CommandSettings, Task<int>>? Delegate { get; }
|
||||
public Func<CommandContext, CommandSettings, CancellationToken, Task<int>>? Delegate { get; }
|
||||
public bool IsDefaultCommand { get; }
|
||||
public bool IsHidden { get; set; }
|
||||
|
||||
@@ -19,7 +19,7 @@ internal sealed class ConfiguredCommand
|
||||
string name,
|
||||
Type? commandType,
|
||||
Type settingsType,
|
||||
Func<CommandContext, CommandSettings, Task<int>>? @delegate,
|
||||
Func<CommandContext, CommandSettings, CancellationToken, Task<int>>? @delegate,
|
||||
bool isDefaultCommand)
|
||||
{
|
||||
Name = name;
|
||||
@@ -60,7 +60,7 @@ internal sealed class ConfiguredCommand
|
||||
}
|
||||
|
||||
public static ConfiguredCommand FromDelegate<TSettings>(
|
||||
string name, Func<CommandContext, CommandSettings, Task<int>>? @delegate = null)
|
||||
string name, Func<CommandContext, CommandSettings, CancellationToken, Task<int>>? @delegate = null)
|
||||
where TSettings : CommandSettings
|
||||
{
|
||||
return new ConfiguredCommand(name, null, typeof(TSettings), @delegate, false);
|
||||
|
||||
@@ -2,16 +2,16 @@ namespace Spectre.Console.Cli;
|
||||
|
||||
internal sealed class DelegateCommand : ICommand
|
||||
{
|
||||
private readonly Func<CommandContext, CommandSettings, Task<int>> _func;
|
||||
private readonly Func<CommandContext, CommandSettings, CancellationToken, Task<int>> _func;
|
||||
|
||||
public DelegateCommand(Func<CommandContext, CommandSettings, Task<int>> func)
|
||||
public DelegateCommand(Func<CommandContext, CommandSettings, CancellationToken, Task<int>> func)
|
||||
{
|
||||
_func = func;
|
||||
}
|
||||
|
||||
public Task<int> Execute(CommandContext context, CommandSettings settings)
|
||||
public Task<int> ExecuteAsync(CommandContext context, CommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return _func(context, settings);
|
||||
return _func(context, settings, cancellationToken);
|
||||
}
|
||||
|
||||
public ValidationResult Validate(CommandContext context, CommandSettings settings)
|
||||
|
||||
@@ -8,7 +8,7 @@ internal sealed class CommandInfo : ICommandContainer, ICommandInfo
|
||||
public object? Data { get; }
|
||||
public Type? CommandType { get; }
|
||||
public Type SettingsType { get; }
|
||||
public Func<CommandContext, CommandSettings, Task<int>>? Delegate { get; }
|
||||
public Func<CommandContext, CommandSettings, CancellationToken, Task<int>>? Delegate { get; }
|
||||
public bool IsDefaultCommand { get; }
|
||||
public CommandInfo? Parent { get; }
|
||||
public IList<CommandInfo> Children { get; }
|
||||
|
||||
@@ -98,7 +98,7 @@ public sealed class CommandAppTester
|
||||
{
|
||||
try
|
||||
{
|
||||
Run(args, Console, c => c.PropagateExceptions());
|
||||
RunAsync(args, Console, c => c.PropagateExceptions()).GetAwaiter().GetResult();
|
||||
throw new InvalidOperationException("Expected an exception to be thrown, but there was none.");
|
||||
}
|
||||
catch (T ex)
|
||||
@@ -129,53 +129,21 @@ public sealed class CommandAppTester
|
||||
/// <returns>The result.</returns>
|
||||
public CommandAppResult Run(params string[] args)
|
||||
{
|
||||
return Run(args, Console);
|
||||
}
|
||||
|
||||
private CommandAppResult Run(string[] args, TestConsole console, Action<IConfigurator>? config = null)
|
||||
{
|
||||
CommandContext? context = null;
|
||||
CommandSettings? settings = null;
|
||||
|
||||
var app = new CommandApp(Registrar);
|
||||
_appConfiguration?.Invoke(app);
|
||||
|
||||
if (_configuration != null)
|
||||
{
|
||||
app.Configure(_configuration);
|
||||
}
|
||||
|
||||
if (config != null)
|
||||
{
|
||||
app.Configure(config);
|
||||
}
|
||||
|
||||
app.Configure(c => c.ConfigureConsole(console));
|
||||
app.Configure(c => c.SetInterceptor(new CallbackCommandInterceptor((ctx, s) =>
|
||||
{
|
||||
context = ctx;
|
||||
settings = s;
|
||||
})));
|
||||
|
||||
var result = app.Run(args);
|
||||
|
||||
var output = console.Output.NormalizeLineEndings();
|
||||
output = TestSettings.TrimConsoleOutput ? output.TrimLines().Trim() : output;
|
||||
|
||||
return new CommandAppResult(result, output, context, settings);
|
||||
return RunAsync(args, Console).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the command application asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="args">The arguments.</param>
|
||||
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
|
||||
/// <returns>The result.</returns>
|
||||
public async Task<CommandAppResult> RunAsync(params string[] args)
|
||||
public async Task<CommandAppResult> RunAsync(string[]? args = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await RunAsync(args, Console);
|
||||
return await RunAsync(args ?? [], Console, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<CommandAppResult> RunAsync(string[] args, TestConsole console, Action<IConfigurator>? config = null)
|
||||
private async Task<CommandAppResult> RunAsync(string[] args, TestConsole console, Action<IConfigurator>? config = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
CommandContext? context = null;
|
||||
CommandSettings? settings = null;
|
||||
@@ -200,7 +168,7 @@ public sealed class CommandAppTester
|
||||
settings = s;
|
||||
})));
|
||||
|
||||
var result = await app.RunAsync(args);
|
||||
var result = await app.RunAsync(args, cancellationToken);
|
||||
|
||||
var output = console.Output.NormalizeLineEndings();
|
||||
output = TestSettings.TrimConsoleOutput ? output.TrimLines().Trim() : output;
|
||||
|
||||
@@ -9,10 +9,10 @@ public sealed class AsynchronousCommand : AsyncCommand<AsynchronousCommandSettin
|
||||
_console = console;
|
||||
}
|
||||
|
||||
public async override Task<int> ExecuteAsync(CommandContext context, AsynchronousCommandSettings settings)
|
||||
public async override Task<int> ExecuteAsync(CommandContext context, AsynchronousCommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
// Simulate a long running asynchronous task
|
||||
await Task.Delay(200);
|
||||
await Task.Delay(200, cancellationToken);
|
||||
|
||||
if (settings.ThrowException)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data;
|
||||
|
||||
public class CatCommand : AnimalCommand<CatSettings>
|
||||
{
|
||||
public override int Execute(CommandContext context, CatSettings settings)
|
||||
public override int Execute(CommandContext context, CatSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class DogCommand : AnimalCommand<DogSettings>
|
||||
return base.Validate(context, settings);
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, DogSettings settings)
|
||||
public override int Execute(CommandContext context, DogSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ public sealed class DumpRemainingCommand : Command<EmptyCommandSettings>
|
||||
_console = console;
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, EmptyCommandSettings settings)
|
||||
public override int Execute(CommandContext context, EmptyCommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
if (context.Remaining.Raw.Count > 0)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data;
|
||||
|
||||
public sealed class EmptyCommand : Command<EmptyCommandSettings>
|
||||
{
|
||||
public override int Execute(CommandContext context, EmptyCommandSettings settings)
|
||||
public override int Execute(CommandContext context, EmptyCommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data;
|
||||
public sealed class GenericCommand<TSettings> : Command<TSettings>
|
||||
where TSettings : CommandSettings
|
||||
{
|
||||
public override int Execute(CommandContext context, TSettings settings)
|
||||
public override int Execute(CommandContext context, TSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data;
|
||||
[Description("The giraffe command.")]
|
||||
public sealed class GiraffeCommand : Command<GiraffeSettings>
|
||||
{
|
||||
public override int Execute(CommandContext context, GiraffeSettings settings)
|
||||
public override int Execute(CommandContext context, GiraffeSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ public class GreeterCommand : Command<OptionalArgumentWithDefaultValueSettings>
|
||||
_console = console;
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings)
|
||||
public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
_console.WriteLine(settings.Greeting);
|
||||
return 0;
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data;
|
||||
|
||||
public sealed class HiddenOptionsCommand : Command<HiddenOptionSettings>
|
||||
{
|
||||
public override int Execute(CommandContext context, HiddenOptionSettings settings)
|
||||
public override int Execute(CommandContext context, HiddenOptionSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data;
|
||||
[Description("The horse command.")]
|
||||
public class HorseCommand : AnimalCommand<HorseSettings>
|
||||
{
|
||||
public override int Execute(CommandContext context, HorseSettings settings)
|
||||
public override int Execute(CommandContext context, HorseSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data;
|
||||
|
||||
public sealed class InvalidCommand : Command<InvalidSettings>
|
||||
{
|
||||
public override int Execute(CommandContext context, InvalidSettings settings)
|
||||
public override int Execute(CommandContext context, InvalidSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data;
|
||||
[Description("The lion command.")]
|
||||
public class LionCommand : AnimalCommand<LionSettings>
|
||||
{
|
||||
public override int Execute(CommandContext context, LionSettings settings)
|
||||
public override int Execute(CommandContext context, LionSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ public sealed class NoDescriptionCommand : Command<EmptyCommandSettings>
|
||||
[CommandOption("-f|--foo <VALUE>")]
|
||||
public int Foo { get; set; }
|
||||
|
||||
public override int Execute(CommandContext context, EmptyCommandSettings settings)
|
||||
public override int Execute(CommandContext context, EmptyCommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data;
|
||||
|
||||
public class OptionVectorCommand : Command<OptionVectorSettings>
|
||||
{
|
||||
public override int Execute(CommandContext context, OptionVectorSettings settings)
|
||||
public override int Execute(CommandContext context, OptionVectorSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data;
|
||||
|
||||
public sealed class ThrowingCommand : Command<ThrowingCommandSettings>
|
||||
{
|
||||
public override int Execute(CommandContext context, ThrowingCommandSettings settings)
|
||||
public override int Execute(CommandContext context, ThrowingCommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new InvalidOperationException("W00t?");
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data;
|
||||
[Description("The turtle command.")]
|
||||
public class TurtleCommand : AnimalCommand<TurtleSettings>
|
||||
{
|
||||
public override int Execute(CommandContext context, TurtleSettings settings)
|
||||
public override int Execute(CommandContext context, TurtleSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ public sealed class VersionCommand : Command<VersionSettings>
|
||||
_console = console;
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, VersionSettings settings)
|
||||
public override int Execute(CommandContext context, VersionSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
_console.WriteLine($"VersionCommand ran, Version: {settings.Version ?? string.Empty}");
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ global using System.Diagnostics.CodeAnalysis;
|
||||
global using System.Globalization;
|
||||
global using System.Linq;
|
||||
global using System.Runtime.CompilerServices;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using Shouldly;
|
||||
global using Spectre.Console.Cli;
|
||||
|
||||
@@ -53,7 +53,7 @@ public sealed partial class CommandAppTests
|
||||
});
|
||||
|
||||
// When
|
||||
var result = await Record.ExceptionAsync(async () =>
|
||||
var exception = await Record.ExceptionAsync(async () =>
|
||||
await app.RunAsync(new[]
|
||||
{
|
||||
"--ThrowException",
|
||||
@@ -61,10 +61,64 @@ public sealed partial class CommandAppTests
|
||||
}));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<Exception>().And(ex =>
|
||||
exception.ShouldBeOfType<Exception>().And(ex =>
|
||||
{
|
||||
ex.Message.ShouldBe("Throwing exception asynchronously");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_Throw_OperationCanceledException_When_Propagated_And_Cancelled()
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.SetDefaultCommand<AsynchronousCommand>();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
});
|
||||
|
||||
// When
|
||||
var exception = await Record.ExceptionAsync(async () =>
|
||||
await app.RunAsync(cancellationToken: new CancellationToken(canceled: true)));
|
||||
|
||||
// Then
|
||||
exception.ShouldNotBeNull();
|
||||
exception.ShouldBeAssignableTo<OperationCanceledException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_Return_Default_Exit_Code_When_Cancelled()
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.SetDefaultCommand<AsynchronousCommand>();
|
||||
|
||||
// When
|
||||
var result = await app.RunAsync(cancellationToken: new CancellationToken(canceled: true));
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(130);
|
||||
result.Output.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Should_Return_Custom_Exit_Code_When_Cancelled()
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.SetDefaultCommand<AsynchronousCommand>();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.CancellationExitCode(123);
|
||||
});
|
||||
|
||||
// When
|
||||
var result = await app.RunAsync(cancellationToken: new CancellationToken(canceled: true));
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(123);
|
||||
result.Output.ShouldBeEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,12 +28,12 @@ public sealed partial class CommandAppTests
|
||||
|
||||
public class NullableCommand : Command<NullableSettings>
|
||||
{
|
||||
public override int Execute(CommandContext context, NullableSettings settings) => 0;
|
||||
public override int Execute(CommandContext context, NullableSettings settings, CancellationToken cancellationToken) => 0;
|
||||
}
|
||||
|
||||
public class NullableWithInitCommand : Command<NullableWithInitSettings>
|
||||
{
|
||||
public override int Execute(CommandContext context, NullableWithInitSettings settings) => 0;
|
||||
public override int Execute(CommandContext context, NullableWithInitSettings settings, CancellationToken cancellationToken) => 0;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -19,7 +19,7 @@ public sealed partial class CommandAppTests
|
||||
_dep = dep;
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, CustomInheritedCommandSettings settings)
|
||||
public override int Execute(CommandContext context, CustomInheritedCommandSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ public sealed partial class CommandAppTests
|
||||
{
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, Settings settings)
|
||||
public override int Execute(CommandContext context, Settings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1117,7 +1117,7 @@ public sealed partial class CommandAppTests
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddDelegate<DogSettings>(
|
||||
"foo", (context, settings) =>
|
||||
"foo", (context, settings, _) =>
|
||||
{
|
||||
dog = settings;
|
||||
data = (int)context.Data;
|
||||
@@ -1145,7 +1145,7 @@ public sealed partial class CommandAppTests
|
||||
{
|
||||
cfg.AddBranch("a", d =>
|
||||
{
|
||||
d.AddDelegate("b", _ => 0);
|
||||
d.AddDelegate("b", (_, _) => 0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1165,7 +1165,7 @@ public sealed partial class CommandAppTests
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(cfg =>
|
||||
{
|
||||
cfg.AddDelegate("a", _ => 0);
|
||||
cfg.AddDelegate("a", (_, _) => 0);
|
||||
});
|
||||
|
||||
// When
|
||||
@@ -1189,7 +1189,7 @@ public sealed partial class CommandAppTests
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddAsyncDelegate<DogSettings>(
|
||||
"foo", (context, settings) =>
|
||||
"foo", (context, settings, _) =>
|
||||
{
|
||||
dog = settings;
|
||||
data = (int)context.Data;
|
||||
@@ -1222,7 +1222,7 @@ public sealed partial class CommandAppTests
|
||||
config.AddBranch<AnimalSettings>("foo", foo =>
|
||||
{
|
||||
foo.AddDelegate<DogSettings>(
|
||||
"bar", (context, settings) =>
|
||||
"bar", (context, settings, _) =>
|
||||
{
|
||||
dog = settings;
|
||||
data = (int)context.Data;
|
||||
@@ -1256,7 +1256,7 @@ public sealed partial class CommandAppTests
|
||||
config.AddBranch<AnimalSettings>("foo", foo =>
|
||||
{
|
||||
foo.AddAsyncDelegate<DogSettings>(
|
||||
"bar", (context, settings) =>
|
||||
"bar", (context, settings, _) =>
|
||||
{
|
||||
dog = settings;
|
||||
data = (int)context.Data;
|
||||
|
||||
@@ -14,7 +14,7 @@ public sealed class CommandAppTesterTests
|
||||
_console = console;
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings)
|
||||
public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
_console.Write(settings.Greeting);
|
||||
return 0;
|
||||
|
||||
@@ -11,7 +11,7 @@ public sealed class InteractiveCommandTests
|
||||
_console = console;
|
||||
}
|
||||
|
||||
public override int Execute(CommandContext context)
|
||||
public override int Execute(CommandContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
var fruits = _console.Prompt(
|
||||
new MultiSelectionPrompt<string>()
|
||||
|
||||
Reference in New Issue
Block a user