mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Use ValueTask
This commit is contained in:
2
.github/workflows/CD.yml
vendored
2
.github/workflows/CD.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Install .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 3.0.100
|
||||
dotnet-version: 3.1.100
|
||||
|
||||
- name: Pack
|
||||
run: dotnet pack CliFx --configuration Release
|
||||
|
||||
2
.github/workflows/CI.yml
vendored
2
.github/workflows/CI.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
- name: Install .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 3.0.100
|
||||
dotnet-version: 3.1.100
|
||||
|
||||
- name: Build & test
|
||||
run: dotnet test --configuration Release
|
||||
|
||||
@@ -4,17 +4,17 @@ using CliFx.Benchmarks.Commands;
|
||||
|
||||
namespace CliFx.Benchmarks
|
||||
{
|
||||
[CoreJob]
|
||||
[SimpleJob]
|
||||
[RankColumn]
|
||||
public class Benchmark
|
||||
{
|
||||
private static readonly string[] Arguments = {"--str", "hello world", "-i", "13", "-b"};
|
||||
|
||||
[Benchmark(Description = "CliFx", Baseline = true)]
|
||||
public Task<int> ExecuteWithCliFx() => new CliApplicationBuilder().AddCommand(typeof(CliFxCommand)).Build().RunAsync(Arguments);
|
||||
public async ValueTask<int> ExecuteWithCliFx() => await new CliApplicationBuilder().AddCommand(typeof(CliFxCommand)).Build().RunAsync(Arguments);
|
||||
|
||||
[Benchmark(Description = "System.CommandLine")]
|
||||
public Task<int> ExecuteWithSystemCommandLine() => new SystemCommandLineCommand().ExecuteAsync(Arguments);
|
||||
public async ValueTask<int> ExecuteWithSystemCommandLine() => await new SystemCommandLineCommand().ExecuteAsync(Arguments);
|
||||
|
||||
[Benchmark(Description = "McMaster.Extensions.CommandLineUtils")]
|
||||
public int ExecuteWithMcMaster() => McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute<McMasterCommand>(Arguments);
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.11.5" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
|
||||
<PackageReference Include="clipr" Version="1.6.1" />
|
||||
<PackageReference Include="CommandLineParser" Version="2.6.0" />
|
||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.3.4" />
|
||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.4.4" />
|
||||
<PackageReference Include="PowerArgs" Version="3.6.0" />
|
||||
<PackageReference Include="System.CommandLine.Experimental" Version="0.3.0-alpha.19317.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -16,6 +16,6 @@ namespace CliFx.Benchmarks.Commands
|
||||
[CommandOption("bool", 'b')]
|
||||
public bool BoolOption { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console) => Task.CompletedTask;
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace CliFx.Demo.Commands
|
||||
_libraryService = libraryService;
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(IConsole console)
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
// To make the demo simpler, we will just generate random publish date and ISBN if they were not set
|
||||
if (Published == default)
|
||||
@@ -48,7 +48,7 @@ namespace CliFx.Demo.Commands
|
||||
console.Output.WriteLine("Book added.");
|
||||
console.RenderBook(book);
|
||||
|
||||
return Task.CompletedTask;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace CliFx.Demo.Commands
|
||||
_libraryService = libraryService;
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(IConsole console)
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
var book = _libraryService.GetBook(Title);
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace CliFx.Demo.Commands
|
||||
|
||||
console.RenderBook(book);
|
||||
|
||||
return Task.CompletedTask;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace CliFx.Demo.Commands
|
||||
_libraryService = libraryService;
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(IConsole console)
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
var library = _libraryService.GetLibrary();
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace CliFx.Demo.Commands
|
||||
console.RenderBook(book);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ namespace CliFx.Demo.Commands
|
||||
_libraryService = libraryService;
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(IConsole console)
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
var book = _libraryService.GetBook(Title);
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace CliFx.Demo.Commands
|
||||
|
||||
console.Output.WriteLine($"Book {Title} removed.");
|
||||
|
||||
return Task.CompletedTask;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,11 +25,11 @@ namespace CliFx.Demo
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
public static Task<int> Main(string[] args)
|
||||
public static async Task<int> Main(string[] args)
|
||||
{
|
||||
var serviceProvider = ConfigureServices();
|
||||
|
||||
return new CliApplicationBuilder()
|
||||
return await new CliApplicationBuilder()
|
||||
.AddCommandsFromThisAssembly()
|
||||
.UseCommandFactory(schema => (ICommand) serviceProvider.GetRequiredService(schema.Type))
|
||||
.Build()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Import Project="../CliFx.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<CollectCoverage>true</CollectCoverage>
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace CliFx.Tests.TestCommands
|
||||
[Command("cancel")]
|
||||
public class CancellableCommand : ICommand
|
||||
{
|
||||
public async Task ExecuteAsync(IConsole console)
|
||||
public async ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
|
||||
@@ -14,6 +14,6 @@ namespace CliFx.Tests.TestCommands
|
||||
[CommandOption("msg", 'm')]
|
||||
public string? Message { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console) => throw new CommandException(Message, ExitCode);
|
||||
public ValueTask ExecuteAsync(IConsole console) => throw new CommandException(Message, ExitCode);
|
||||
}
|
||||
}
|
||||
@@ -14,10 +14,10 @@ namespace CliFx.Tests.TestCommands
|
||||
[CommandOption('s', Description = "String separator.")]
|
||||
public string Separator { get; set; } = "";
|
||||
|
||||
public Task ExecuteAsync(IConsole console)
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
console.Output.WriteLine(string.Join(Separator, Inputs));
|
||||
return Task.CompletedTask;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,10 @@ namespace CliFx.Tests.TestCommands
|
||||
// This property should be ignored by resolver
|
||||
public bool NotAnOption { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console)
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
console.Output.WriteLine(Dividend / Divisor);
|
||||
return Task.CompletedTask;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,6 @@ namespace CliFx.Tests.TestCommands
|
||||
[CommandOption("fruits")]
|
||||
public string? Oranges { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console) => Task.CompletedTask;
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,6 @@ namespace CliFx.Tests.TestCommands
|
||||
[CommandOption('f')]
|
||||
public string? Oranges { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console) => Task.CompletedTask;
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,6 @@ namespace CliFx.Tests.TestCommands
|
||||
[CommandOption("opt", EnvironmentVariableName = "ENV_SINGLE_VALUE")]
|
||||
public string? Option { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console) => Task.CompletedTask;
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,6 @@ namespace CliFx.Tests.TestCommands
|
||||
[CommandOption("opt", EnvironmentVariableName = "ENV_MULTIPLE_VALUES")]
|
||||
public IEnumerable<string>? Option { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console) => Task.CompletedTask;
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,6 @@ namespace CliFx.Tests.TestCommands
|
||||
[CommandOption("opt", EnvironmentVariableName = "ENV_MULTIPLE_VALUES")]
|
||||
public string? Option { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console) => Task.CompletedTask;
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,6 @@ namespace CliFx.Tests.TestCommands
|
||||
[CommandOption("msg", 'm')]
|
||||
public string? Message { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console) => throw new Exception(Message);
|
||||
public ValueTask ExecuteAsync(IConsole console) => throw new Exception(Message);
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,10 @@ namespace CliFx.Tests.TestCommands
|
||||
[Command]
|
||||
public class HelloWorldDefaultCommand : ICommand
|
||||
{
|
||||
public Task ExecuteAsync(IConsole console)
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
console.Output.WriteLine("Hello world.");
|
||||
return Task.CompletedTask;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,6 @@ namespace CliFx.Tests.TestCommands
|
||||
[CommandOption("option-b", 'b', Description = "OptionB description.")]
|
||||
public string? OptionB { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console) => Task.CompletedTask;
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,6 @@ namespace CliFx.Tests.TestCommands
|
||||
[CommandOption("option-d", 'd', Description = "OptionD description.")]
|
||||
public string? OptionD { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console) => Task.CompletedTask;
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,6 @@ namespace CliFx.Tests.TestCommands
|
||||
[CommandOption("option-e", 'e', Description = "OptionE description.")]
|
||||
public string? OptionE { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console) => Task.CompletedTask;
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,6 @@ namespace CliFx.Tests.TestCommands
|
||||
{
|
||||
public class NonAnnotatedCommand : ICommand
|
||||
{
|
||||
public Task ExecuteAsync(IConsole console) => Task.CompletedTask;
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ namespace CliFx
|
||||
_helpTextRenderer = helpTextRenderer;
|
||||
}
|
||||
|
||||
private async Task<int?> HandleDebugDirectiveAsync(CommandInput commandInput)
|
||||
private async ValueTask<int?> HandleDebugDirectiveAsync(CommandInput commandInput)
|
||||
{
|
||||
// Debug mode is enabled if it's allowed in the application and it was requested via corresponding directive
|
||||
var isDebugMode = _configuration.IsDebugModeAllowed && commandInput.IsDebugDirectiveSpecified();
|
||||
@@ -162,7 +162,7 @@ namespace CliFx
|
||||
return isError ? -1 : 0;
|
||||
}
|
||||
|
||||
private async Task<int> HandleCommandExecutionAsync(CommandInput commandInput, CommandSchema targetCommandSchema)
|
||||
private async ValueTask<int> HandleCommandExecutionAsync(CommandInput commandInput, CommandSchema targetCommandSchema)
|
||||
{
|
||||
// Create an instance of the command
|
||||
var command = _commandFactory.CreateCommand(targetCommandSchema);
|
||||
@@ -178,7 +178,7 @@ namespace CliFx
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<int> RunAsync(IReadOnlyList<string> commandLineArguments)
|
||||
public async ValueTask<int> RunAsync(IReadOnlyList<string> commandLineArguments)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Import Project="../CliFx.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net45;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.1;netstandard2.0;net45</TargetFrameworks>
|
||||
<Authors>$(Company)</Authors>
|
||||
<Description>Declarative framework for CLI applications</Description>
|
||||
<PackageTags>command line executable interface framework parser arguments net core</PackageTags>
|
||||
@@ -18,15 +18,18 @@
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0-preview.2" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19554-01" PrivateAssets="all" />
|
||||
<PackageReference Include="Nullable" Version="1.1.1" PrivateAssets="all" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../favicon.png" Pack="True" PackagePath="" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Nullable" Version="1.1.1" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net45'">
|
||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -11,6 +11,6 @@ namespace CliFx
|
||||
/// <summary>
|
||||
/// Runs application with specified command line arguments and returns an exit code.
|
||||
/// </summary>
|
||||
Task<int> RunAsync(IReadOnlyList<string> commandLineArguments);
|
||||
ValueTask<int> RunAsync(IReadOnlyList<string> commandLineArguments);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,6 @@ namespace CliFx
|
||||
/// Executes command using specified implementation of <see cref="IConsole"/>.
|
||||
/// This method is called when the command is invoked by a user through command line interface.
|
||||
/// </summary>
|
||||
Task ExecuteAsync(IConsole console);
|
||||
ValueTask ExecuteAsync(IConsole console);
|
||||
}
|
||||
}
|
||||
26
Readme.md
26
Readme.md
@@ -59,8 +59,8 @@ The following code will create and run default `CliApplication` that will resolv
|
||||
```c#
|
||||
public static class Program
|
||||
{
|
||||
public static Task<int> Main(string[] args) =>
|
||||
new CliApplicationBuilder()
|
||||
public static async Task<int> Main(string[] args) =>
|
||||
await new CliApplicationBuilder()
|
||||
.AddCommandsFromThisAssembly()
|
||||
.Build()
|
||||
.RunAsync(args);
|
||||
@@ -85,17 +85,17 @@ public class LogCommand : ICommand
|
||||
[CommandOption("base", 'b', Description = "Logarithm base.")]
|
||||
public double Base { get; set; } = 10;
|
||||
|
||||
public Task ExecuteAsync(IConsole console)
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
var result = Math.Log(Value, Base);
|
||||
console.Output.WriteLine(result);
|
||||
|
||||
return Task.CompletedTask;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By implementing `ICommand` this class also provides `ExecuteAsync` method. This is the method that gets called when the user invokes the command. Its return type is `Task` in order to facilitate asynchronous execution, but if your command runs synchronously you can simply return `Task.CompletedTask`.
|
||||
By implementing `ICommand` this class also provides `ExecuteAsync` method. This is the method that gets called when the user invokes the command. Its return type is `ValueTask` in order to facilitate both synchronous and asynchronous execution. If your command always runs synchronously, simply return `default` at the end of the method.
|
||||
|
||||
The `ExecuteAsync` method also takes an instance of `IConsole` as a parameter. You should use the `console` parameter in places where you would normally use `System.Console`, in order to make your command testable.
|
||||
|
||||
@@ -171,7 +171,7 @@ public class DivideCommand : ICommand
|
||||
[CommandOption("divisor", IsRequired = true)]
|
||||
public double Divisor { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console)
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
if (Math.Abs(Divisor) < double.Epsilon)
|
||||
{
|
||||
@@ -182,7 +182,7 @@ public class DivideCommand : ICommand
|
||||
var result = Dividend / Divisor;
|
||||
console.Output.WriteLine(result);
|
||||
|
||||
return Task.CompletedTask;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -225,7 +225,7 @@ Cancelled or terminated app returns non-zero exit code.
|
||||
[Command("cancel")]
|
||||
public class CancellableCommand : ICommand
|
||||
{
|
||||
public async Task ExecuteAsync(IConsole console)
|
||||
public async ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
console.Output.WriteLine("Printed");
|
||||
|
||||
@@ -248,7 +248,7 @@ For example, here is how you would configure your application to use [`Microsoft
|
||||
```c#
|
||||
public static class Program
|
||||
{
|
||||
public static Task<int> Main(string[] args)
|
||||
public static async Task<int> Main(string[] args)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
||||
@@ -260,7 +260,7 @@ public static class Program
|
||||
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
return new CliApplicationBuilder()
|
||||
return await new CliApplicationBuilder()
|
||||
.AddCommandsFromThisAssembly()
|
||||
.UseCommandFactory(schema => (ICommand) serviceProvider.GetRequiredService(schema.Type))
|
||||
.Build()
|
||||
@@ -285,7 +285,7 @@ public class UserAddCommand : ICommand
|
||||
[CommandOption("email", 'e')]
|
||||
public string Email { get; set; }
|
||||
|
||||
public Task ExecuteAsync(IConsole console)
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
var validationResult = new UserAddCommandValidator().Validate(this);
|
||||
if (!validationResult.IsValid)
|
||||
@@ -358,13 +358,13 @@ public class ConcatCommand : ICommand
|
||||
[CommandOption("right")]
|
||||
public string Right { get; set; } = "world";
|
||||
|
||||
public Task ExecuteAsync(IConsole console)
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
console.Output.Write(Left);
|
||||
console.Output.Write(' ');
|
||||
console.Output.Write(Right);
|
||||
|
||||
return Task.CompletedTask;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user