mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 00:42:51 +08:00
Implemented AddAsyncDelegate
(#766)
This commit is contained in:
parent
0ec70a44db
commit
35ce60b596
@ -9,7 +9,7 @@ public static partial class Program
|
||||
{
|
||||
[CommandOption("--count")]
|
||||
[Description("The number of bars to print")]
|
||||
[DefaultValue(1)]
|
||||
[DefaultValue(3)]
|
||||
public int Count { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System.Threading.Tasks;
|
||||
using Spectre.Console;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
@ -14,7 +15,13 @@ public static partial class Program
|
||||
.WithDescription("Foos the bars");
|
||||
|
||||
config.AddDelegate<BarSettings>("bar", Bar)
|
||||
.WithDescription("Bars the foos"); ;
|
||||
.WithDescription("Bars the foos");
|
||||
|
||||
config.AddAsyncDelegate("fooAsync", FooAsync)
|
||||
.WithDescription("Foos the bars asynchronously");
|
||||
|
||||
config.AddAsyncDelegate<BarSettings>("barAsync", BarAsync)
|
||||
.WithDescription("Bars the foos asynchronously");
|
||||
});
|
||||
|
||||
return app.Run(args);
|
||||
@ -35,4 +42,20 @@ public static partial class Program
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static Task<int> FooAsync(CommandContext context)
|
||||
{
|
||||
AnsiConsole.WriteLine("Foo");
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
private static Task<int> BarAsync(CommandContext context, BarSettings settings)
|
||||
{
|
||||
for (var index = 0; index < settings.Count; index++)
|
||||
{
|
||||
AnsiConsole.WriteLine("Bar");
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Shared\Shared.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\Spectre.Console.Json\Spectre.Console.Json.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -81,6 +81,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Layout", "Console\Layout\La
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Json", "Console\Json\Json.csproj", "{ABE3E734-0756-4D5A-B28A-E6E526D9927D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Json", "..\src\Spectre.Console.Json\Spectre.Console.Json.csproj", "{91A5637F-1F89-48B3-A0BA-6CC629807393}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -535,6 +537,18 @@ Global
|
||||
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|x64.Build.0 = Release|Any CPU
|
||||
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|x86.Build.0 = Release|Any CPU
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393}.Release|x64.Build.0 = Release|Any CPU
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -549,6 +563,7 @@ Global
|
||||
{0C58FB17-F60A-47AB-84BF-961EC8C06AE6} = {2571F1BD-6556-4F96-B27B-B6190E1BF13A}
|
||||
{A127CE7D-A5A7-4745-9809-EBD7CB12CEE7} = {2571F1BD-6556-4F96-B27B-B6190E1BF13A}
|
||||
{EFAADF6A-C77D-41EC-83F5-BBB4FFC5A6D7} = {2571F1BD-6556-4F96-B27B-B6190E1BF13A}
|
||||
{91A5637F-1F89-48B3-A0BA-6CC629807393} = {2571F1BD-6556-4F96-B27B-B6190E1BF13A}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3EE724C5-CAB4-410D-AC63-8D4260EF83ED}
|
||||
|
@ -237,6 +237,26 @@ public static class ConfiguratorExtensions
|
||||
return configurator.AddDelegate<EmptyCommandSettings>(name, (c, _) => func(c));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command without settings that executes an async delegate.
|
||||
/// </summary>
|
||||
/// <param name="configurator">The configurator.</param>
|
||||
/// <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>
|
||||
public static ICommandConfigurator AddAsyncDelegate(
|
||||
this IConfigurator configurator,
|
||||
string name,
|
||||
Func<CommandContext, Task<int>> func)
|
||||
{
|
||||
if (configurator == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurator));
|
||||
}
|
||||
|
||||
return configurator.AddAsyncDelegate<EmptyCommandSettings>(name, (c, _) => func(c));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command without settings that executes a delegate.
|
||||
/// </summary>
|
||||
@ -259,6 +279,28 @@ public static class ConfiguratorExtensions
|
||||
return configurator.AddDelegate<TSettings>(name, (c, _) => func(c));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command without settings that executes an async delegate.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSettings">The command setting type.</typeparam>
|
||||
/// <param name="configurator">The configurator.</param>
|
||||
/// <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>
|
||||
public static ICommandConfigurator AddAsyncDelegate<TSettings>(
|
||||
this IConfigurator<TSettings> configurator,
|
||||
string name,
|
||||
Func<CommandContext, Task<int>> func)
|
||||
where TSettings : CommandSettings
|
||||
{
|
||||
if (configurator == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurator));
|
||||
}
|
||||
|
||||
return configurator.AddAsyncDelegate<TSettings>(name, (c, _) => func(c));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the ExceptionsHandler.
|
||||
/// <para>Setting <see cref="ICommandAppSettings.ExceptionHandler"/> this way will use the
|
||||
|
@ -35,6 +35,16 @@ public interface IConfigurator
|
||||
ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, int> func)
|
||||
where TSettings : CommandSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command that executes an async delegate.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSettings">The command setting type.</typeparam>
|
||||
/// <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)
|
||||
where TSettings : CommandSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command branch.
|
||||
/// </summary>
|
||||
|
@ -57,6 +57,16 @@ public interface IConfigurator<in TSettings>
|
||||
ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, int> func)
|
||||
where TDerivedSettings : TSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command that executes an async delegate.
|
||||
/// </summary>
|
||||
/// <typeparam name="TDerivedSettings">The derived command setting type.</typeparam>
|
||||
/// <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)
|
||||
where TDerivedSettings : TSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command branch.
|
||||
/// </summary>
|
||||
|
@ -42,6 +42,14 @@ internal sealed class Configurator : IUnsafeConfigurator, IConfigurator, IConfig
|
||||
|
||||
public ICommandConfigurator AddDelegate<TSettings>(string name, Func<CommandContext, TSettings, int> func)
|
||||
where TSettings : CommandSettings
|
||||
{
|
||||
var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>(
|
||||
name, (context, settings) => Task.FromResult(func(context, (TSettings)settings))));
|
||||
return new CommandConfigurator(command);
|
||||
}
|
||||
|
||||
public ICommandConfigurator AddAsyncDelegate<TSettings>(string name, Func<CommandContext, TSettings, Task<int>> func)
|
||||
where TSettings : CommandSettings
|
||||
{
|
||||
var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>(
|
||||
name, (context, settings) => func(context, (TSettings)settings)));
|
||||
|
@ -27,6 +27,7 @@ internal sealed class Configurator<TSettings> : IUnsafeBranchConfigurator, IConf
|
||||
{
|
||||
var defaultCommand = ConfiguredCommand.FromType<TDefaultCommand>(
|
||||
CliConstants.DefaultCommandName, isDefaultCommand: true);
|
||||
|
||||
_command.Children.Add(defaultCommand);
|
||||
}
|
||||
|
||||
@ -47,6 +48,16 @@ internal sealed class Configurator<TSettings> : IUnsafeBranchConfigurator, IConf
|
||||
|
||||
public ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, int> func)
|
||||
where TDerivedSettings : TSettings
|
||||
{
|
||||
var command = ConfiguredCommand.FromDelegate<TDerivedSettings>(
|
||||
name, (context, settings) => Task.FromResult(func(context, (TDerivedSettings)settings)));
|
||||
|
||||
_command.Children.Add(command);
|
||||
return new CommandConfigurator(command);
|
||||
}
|
||||
|
||||
public ICommandConfigurator AddAsyncDelegate<TDerivedSettings>(string name, Func<CommandContext, TDerivedSettings, Task<int>> func)
|
||||
where TDerivedSettings : TSettings
|
||||
{
|
||||
var command = ConfiguredCommand.FromDelegate<TDerivedSettings>(
|
||||
name, (context, settings) => func(context, (TDerivedSettings)settings));
|
||||
|
@ -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, int>? Delegate { get; }
|
||||
public Func<CommandContext, CommandSettings, 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, int>? @delegate,
|
||||
Func<CommandContext, CommandSettings, 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, int>? @delegate = null)
|
||||
string name, Func<CommandContext, CommandSettings, 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, int> _func;
|
||||
private readonly Func<CommandContext, CommandSettings, Task<int>> _func;
|
||||
|
||||
public DelegateCommand(Func<CommandContext, CommandSettings, int> func)
|
||||
public DelegateCommand(Func<CommandContext, CommandSettings, Task<int>> func)
|
||||
{
|
||||
_func = func;
|
||||
}
|
||||
|
||||
public Task<int> Execute(CommandContext context, CommandSettings settings)
|
||||
{
|
||||
return Task.FromResult(_func(context, settings));
|
||||
return _func(context, settings);
|
||||
}
|
||||
|
||||
public ValidationResult Validate(CommandContext context, CommandSettings settings)
|
||||
|
@ -8,7 +8,7 @@ internal sealed class CommandInfo : ICommandContainer
|
||||
public object? Data { get; }
|
||||
public Type? CommandType { get; }
|
||||
public Type SettingsType { get; }
|
||||
public Func<CommandContext, CommandSettings, int>? Delegate { get; }
|
||||
public Func<CommandContext, CommandSettings, Task<int>>? Delegate { get; }
|
||||
public bool IsDefaultCommand { get; }
|
||||
public CommandInfo? Parent { get; }
|
||||
public IList<CommandInfo> Children { get; }
|
||||
|
@ -1135,6 +1135,37 @@ public sealed partial class CommandAppTests
|
||||
data.ShouldBe(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void Should_Execute_Async_Delegate_Command_At_Root_Level()
|
||||
{
|
||||
// Given
|
||||
var dog = default(DogSettings);
|
||||
var data = 0;
|
||||
|
||||
var app = new CommandApp();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddAsyncDelegate<DogSettings>(
|
||||
"foo", (context, settings) =>
|
||||
{
|
||||
dog = settings;
|
||||
data = (int)context.Data;
|
||||
return Task.FromResult(1);
|
||||
}).WithData(2);
|
||||
});
|
||||
|
||||
// When
|
||||
var result = await app.RunAsync(new[] { "foo", "4", "12" });
|
||||
|
||||
// Then
|
||||
result.ShouldBe(1);
|
||||
dog.ShouldNotBeNull();
|
||||
dog.Age.ShouldBe(12);
|
||||
dog.Legs.ShouldBe(4);
|
||||
data.ShouldBe(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Execute_Nested_Delegate_Command()
|
||||
{
|
||||
@ -1168,5 +1199,39 @@ public sealed partial class CommandAppTests
|
||||
dog.Legs.ShouldBe(4);
|
||||
data.ShouldBe(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void Should_Execute_Nested_Async_Delegate_Command()
|
||||
{
|
||||
// Given
|
||||
var dog = default(DogSettings);
|
||||
var data = 0;
|
||||
|
||||
var app = new CommandApp();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddBranch<AnimalSettings>("foo", foo =>
|
||||
{
|
||||
foo.AddAsyncDelegate<DogSettings>(
|
||||
"bar", (context, settings) =>
|
||||
{
|
||||
dog = settings;
|
||||
data = (int)context.Data;
|
||||
return Task.FromResult(1);
|
||||
}).WithData(2);
|
||||
});
|
||||
});
|
||||
|
||||
// When
|
||||
var result = await app.RunAsync(new[] { "foo", "4", "bar", "12" });
|
||||
|
||||
// Then
|
||||
result.ShouldBe(1);
|
||||
dog.ShouldNotBeNull();
|
||||
dog.Age.ShouldBe(12);
|
||||
dog.Legs.ShouldBe(4);
|
||||
data.ShouldBe(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user