mirror of
https://github.com/spectreconsole/spectre.console.git
synced 2025-10-25 15:19:23 +00:00
Compare commits
12 Commits
0.51.1
...
4d7c7a72cf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d7c7a72cf | ||
|
|
749f0fded8 | ||
|
|
f5f61ca610 | ||
|
|
d90e94dbb3 | ||
|
|
169abca986 | ||
|
|
3c2156268c | ||
|
|
6fb81103f0 | ||
|
|
880e83b27c | ||
|
|
0b270e1ccd | ||
|
|
2d9e8069fd | ||
|
|
b551bbd244 | ||
|
|
3a70fbec75 |
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@@ -18,12 +18,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup .NET SDK
|
- name: Setup .NET SDK
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v5
|
||||||
with:
|
with:
|
||||||
dotnet-version: |
|
dotnet-version: |
|
||||||
8.0.x
|
8.0.x
|
||||||
|
|||||||
12
.github/workflows/publish.yaml
vendored
12
.github/workflows/publish.yaml
vendored
@@ -24,12 +24,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup .NET SDK
|
- name: Setup .NET SDK
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v5
|
||||||
with:
|
with:
|
||||||
dotnet-version: |
|
dotnet-version: |
|
||||||
8.0.x
|
8.0.x
|
||||||
@@ -53,17 +53,17 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup .NET SDK
|
- name: Setup .NET SDK
|
||||||
uses: actions/setup-dotnet@v4
|
uses: actions/setup-dotnet@v5
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v5
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '22'
|
||||||
|
|
||||||
- name: Cache dependencies
|
- name: Cache dependencies
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
|
|||||||
@@ -38,8 +38,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Playwright" Version="1.52.0" />
|
<PackageReference Include="Microsoft.Playwright" Version="1.55.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
<PackageReference Include="Statiq.CodeAnalysis" Version="1.0.0-beta.72" />
|
<PackageReference Include="Statiq.CodeAnalysis" Version="1.0.0-beta.72" />
|
||||||
<PackageReference Include="Statiq.Common" Version="1.0.0-beta.72" />
|
<PackageReference Include="Statiq.Common" Version="1.0.0-beta.72" />
|
||||||
<PackageReference Include="Statiq.Web" Version="1.0.0-beta.60" />
|
<PackageReference Include="Statiq.Web" Version="1.0.0-beta.60" />
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 16
|
|
||||||
VisualStudioVersion = 16.0.30011.22
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Docs", "Docs.csproj", "{C337F609-A890-4E52-BDA3-91658039B0E3}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{C337F609-A890-4E52-BDA3-91658039B0E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{C337F609-A890-4E52-BDA3-91658039B0E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{C337F609-A890-4E52-BDA3-91658039B0E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{C337F609-A890-4E52-BDA3-91658039B0E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {2FB3922B-494A-45EB-A479-FC507B8E107C}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
3
docs/Docs.slnx
Normal file
3
docs/Docs.slnx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<Solution>
|
||||||
|
<Project Path="Docs.csproj" />
|
||||||
|
</Solution>
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"sdk": {
|
|
||||||
"version": "9.0.202",
|
|
||||||
"rollForward": "latestFeature"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
Title: Spectre.Console 0.51 released!
|
Title: Spectre.Console 0.51.1 released!
|
||||||
Description: Not a substitute for human interaction.
|
Description: Not a substitute for human interaction.
|
||||||
Published: 2025-09-07
|
Published: 2025-09-07
|
||||||
Category: Release Notes
|
Category: Release Notes
|
||||||
Excluded: false
|
Excluded: false
|
||||||
---
|
---
|
||||||
|
|
||||||
Version 0.51 of Spectre.Console has been released!
|
Version `0.51.1` of Spectre.Console has been released!
|
||||||
|
|
||||||
|
_Note: Due to an issue discovered after the release of version 0.51.0, that version has now been unlisted. Let’s all pretend it never existed 😅_
|
||||||
|
|
||||||
## What's Changed
|
## What's Changed
|
||||||
|
|
||||||
@@ -38,4 +40,4 @@ Version 0.51 of Spectre.Console has been released!
|
|||||||
* [@mitchdenny](https://github.com/mitchdenny) made their first contribution in [#1881](https://github.com/spectreconsole/spectre.console/pull/1881)
|
* [@mitchdenny](https://github.com/mitchdenny) made their first contribution in [#1881](https://github.com/spectreconsole/spectre.console/pull/1881)
|
||||||
* [@mattfennerom](https://github.com/mattfennerom) made their first contribution in [#1890](https://github.com/spectreconsole/spectre.console/pull/1890)
|
* [@mattfennerom](https://github.com/mattfennerom) made their first contribution in [#1890](https://github.com/spectreconsole/spectre.console/pull/1890)
|
||||||
|
|
||||||
**Full Changelog**: [0.50.0...0.51.0](https://github.com/spectreconsole/spectre.console/compare/0.50.0...0.51.0)
|
**Full Changelog**: [0.50.0...0.51.0](https://github.com/spectreconsole/spectre.console/compare/0.50.0...0.51.1)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
Title: Spectre.Console 0.52.0 released!
|
||||||
|
Description: Don't eat (too much) glue.
|
||||||
|
Published: 2025-10-10
|
||||||
|
Category: Release Notes
|
||||||
|
Excluded: false
|
||||||
|
---
|
||||||
|
|
||||||
|
Version `0.52.0` of Spectre.Console has been released!
|
||||||
|
|
||||||
|
Exciting things are happening. We’ve merged support for my love child, OpenCli, in this release. That means you can now pass the parameter `--help-dump-opencli` to your application to get an [OpenCli](https://opencli.org) description dumped to stdout.
|
||||||
|
|
||||||
|
## What's Changed
|
||||||
|
|
||||||
|
* Add OpenCLI integration to Spectre.Console.Cli by [@patriksvensson](https://github.com/patriksvensson) in [#1909](https://github.com/spectreconsole/spectre.console/pull/1909)
|
||||||
|
* Fix OPENCLI_VISIBILITY_INTERNAL to DefineConstants concat by [@devlead](https://github.com/devlead) in [#1912](https://github.com/spectreconsole/spectre.console/pull/1912)
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/spectreconsole/spectre.console/compare/0.51.1...0.52.0
|
||||||
22
docs/input/cli/opencli.md
Normal file
22
docs/input/cli/opencli.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Title: OpenCLI Integration
|
||||||
|
Order: 15
|
||||||
|
Description: OpenCLI integration
|
||||||
|
Highlights:
|
||||||
|
- Generate OpenCLI descriptions
|
||||||
|
---
|
||||||
|
|
||||||
|
From version `0.52.0` and above, you will be able to generate [OpenCLI](https://opencli.org)
|
||||||
|
descriptions from your `Spectre.Console.Cli` applications.
|
||||||
|
|
||||||
|
Simply add the `--help-dump-opencli` option to your application, and an
|
||||||
|
OpenCLI description will be written to stdout.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ ./myapp --help-dump-opencli
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to save it to disk, pipe it to a file.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ ./myapp --help-dump-opencli > myapp.openapi.json
|
||||||
|
```
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"cake.tool": {
|
"cake.tool": {
|
||||||
"version": "5.0.0",
|
"version": "5.1.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
"dotnet-cake"
|
"dotnet-cake"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json.schemastore.org/global",
|
"$schema": "http://json.schemastore.org/global",
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "9.0.202",
|
"version": "9.0.305",
|
||||||
"rollForward": "latestFeature"
|
"rollForward": "latestFeature"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
using Generator.Models;
|
using Generator.Models;
|
||||||
using Scriban;
|
using Scriban;
|
||||||
using Spectre.Console.Cli;
|
using Spectre.Console.Cli;
|
||||||
@@ -21,7 +22,7 @@ namespace Generator.Commands
|
|||||||
public string Input { get; set; }
|
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[]
|
var templates = new FilePath[]
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AngleSharp.Html.Parser;
|
using AngleSharp.Html.Parser;
|
||||||
using Generator.Models;
|
using Generator.Models;
|
||||||
@@ -39,7 +40,7 @@ namespace Generator.Commands
|
|||||||
_parser = new HtmlParser();
|
_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);
|
var output = new DirectoryPath(settings.Output);
|
||||||
if (!_fileSystem.Directory.Exists(settings.Output))
|
if (!_fileSystem.Directory.Exists(settings.Output))
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using Generator.Commands.Samples;
|
using Generator.Commands.Samples;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
using Spectre.Console.Cli;
|
using Spectre.Console.Cli;
|
||||||
@@ -38,7 +39,7 @@ namespace Generator.Commands
|
|||||||
_console = new AsciiCastConsole(console);
|
_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
|
var samples = typeof(BaseSample).Assembly
|
||||||
.GetTypes()
|
.GetTypes()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
using Generator.Models;
|
using Generator.Models;
|
||||||
using Scriban;
|
using Scriban;
|
||||||
using Spectre.Console.Cli;
|
using Spectre.Console.Cli;
|
||||||
@@ -16,7 +17,7 @@ namespace Generator.Commands
|
|||||||
_fileSystem = new FileSystem();
|
_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.
|
// Read the spinner model.
|
||||||
var spinners = new List<Spinner>();
|
var spinners = new List<Spinner>();
|
||||||
|
|||||||
@@ -45,9 +45,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AngleSharp" Version="1.3.0" />
|
<PackageReference Include="AngleSharp" Version="1.3.0" />
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
<PackageReference Include="Scriban" Version="6.2.1" />
|
<PackageReference Include="Scriban" Version="6.4.0" />
|
||||||
<PackageReference Include="Spectre.IO" Version="0.18.0" />
|
<PackageReference Include="Spectre.IO" Version="0.20.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -100,5 +100,8 @@ dotnet_diagnostic.RCS1047.severity = none
|
|||||||
# RCS1090: Call 'ConfigureAwait(false)'.
|
# RCS1090: Call 'ConfigureAwait(false)'.
|
||||||
dotnet_diagnostic.RCS1090.severity = warning
|
dotnet_diagnostic.RCS1090.severity = warning
|
||||||
|
|
||||||
# The file header is missing or not located at the top of the file
|
# SA1633: The file header is missing or not located at the top of the file
|
||||||
dotnet_diagnostic.SA1633.severity = none
|
dotnet_diagnostic.SA1633.severity = none
|
||||||
|
|
||||||
|
# CA2016: Forward the CancellationToken parameter to methods that take one
|
||||||
|
dotnet_diagnostic.CA2016.severity = warning
|
||||||
@@ -4,22 +4,23 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="IsExternalInit" Version="1.0.3" />
|
<PackageVersion Include="IsExternalInit" Version="1.0.3" />
|
||||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.3" />
|
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.9" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
|
||||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="8.0.0" />
|
<PackageVersion Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="8.0.0" />
|
||||||
<PackageVersion Include="MinVer" PrivateAssets="All" Version="6.0.0" />
|
<PackageVersion Include="MinVer" PrivateAssets="All" Version="6.0.0" />
|
||||||
|
<PackageVersion Include="OpenCli.Sources" Version="0.5.0" />
|
||||||
<PackageVersion Include="PolySharp" Version="1.15.0" />
|
<PackageVersion Include="PolySharp" Version="1.15.0" />
|
||||||
<PackageVersion Include="Roslynator.Analyzers" PrivateAssets="All" Version="4.14.0" />
|
<PackageVersion Include="Roslynator.Analyzers" PrivateAssets="All" Version="4.14.1" />
|
||||||
<PackageVersion Include="Shouldly" Version="4.3.0" />
|
<PackageVersion Include="Shouldly" Version="4.3.0" />
|
||||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" />
|
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.11" />
|
||||||
<PackageVersion Include="Spectre.Verify.Extensions" Version="28.16.0" />
|
<PackageVersion Include="Spectre.Verify.Extensions" Version="28.16.0" />
|
||||||
<PackageVersion Include="StyleCop.Analyzers" PrivateAssets="All" Version="1.2.0-beta.556" />
|
<PackageVersion Include="StyleCop.Analyzers" PrivateAssets="All" Version="1.2.0-beta.556" />
|
||||||
<PackageVersion Include="System.Memory" Version="4.6.3" />
|
<PackageVersion Include="System.Memory" Version="4.6.3" />
|
||||||
<PackageVersion Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" Version="1.0.0-alpha.160" />
|
<PackageVersion Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" Version="1.0.0-alpha.160" />
|
||||||
<PackageVersion Include="Verify.Xunit" Version="30.5.0" />
|
<PackageVersion Include="Verify.Xunit" Version="31.0.2" />
|
||||||
<PackageVersion Include="Wcwidth.Sources" Version="2.0.0" />
|
<PackageVersion Include="Wcwidth.Sources" Version="3.0.0" />
|
||||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.3">
|
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageVersion>
|
</PackageVersion>
|
||||||
|
|||||||
@@ -9,19 +9,20 @@ public abstract class AsyncCommand : ICommand<EmptyCommandSettings>
|
|||||||
/// Executes the command.
|
/// Executes the command.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The command context.</param>
|
/// <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>
|
/// <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/>
|
/// <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/>
|
/// <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/>
|
/// <inheritdoc/>
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ public abstract class AsyncCommand<TSettings> : ICommand<TSettings>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The command context.</param>
|
/// <param name="context">The command context.</param>
|
||||||
/// <param name="settings">The settings.</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>
|
/// <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/>
|
/// <inheritdoc/>
|
||||||
ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings)
|
ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings)
|
||||||
@@ -33,15 +34,15 @@ public abstract class AsyncCommand<TSettings> : ICommand<TSettings>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <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.");
|
Debug.Assert(settings is TSettings, "Command settings is of unexpected type.");
|
||||||
return ExecuteAsync(context, (TSettings)settings);
|
return ExecuteAsync(context, (TSettings)settings, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <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.
|
/// Executes the command.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The command context.</param>
|
/// <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>
|
/// <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/>
|
/// <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/>
|
/// <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/>
|
/// <inheritdoc/>
|
||||||
|
|||||||
@@ -26,10 +26,7 @@ public sealed class CommandApp : ICommandApp
|
|||||||
_executor = new CommandExecutor(registrar);
|
_executor = new CommandExecutor(registrar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Configures the command line application.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configuration">The configuration.</param>
|
|
||||||
public void Configure(Action<IConfigurator> configuration)
|
public void Configure(Action<IConfigurator> configuration)
|
||||||
{
|
{
|
||||||
if (configuration == null)
|
if (configuration == null)
|
||||||
@@ -51,22 +48,14 @@ public sealed class CommandApp : ICommandApp
|
|||||||
return new DefaultCommandConfigurator(GetConfigurator().SetDefaultCommand<TCommand>());
|
return new DefaultCommandConfigurator(GetConfigurator().SetDefaultCommand<TCommand>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Runs the command line application with specified arguments.
|
public int Run(IEnumerable<string> args, CancellationToken cancellationToken = default)
|
||||||
/// </summary>
|
|
||||||
/// <param name="args">The arguments.</param>
|
|
||||||
/// <returns>The exit code from the executed command.</returns>
|
|
||||||
public int Run(IEnumerable<string> args)
|
|
||||||
{
|
{
|
||||||
return RunAsync(args).GetAwaiter().GetResult();
|
return RunAsync(args, cancellationToken).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Runs the command line application with specified arguments.
|
public async Task<int> RunAsync(IEnumerable<string> args, CancellationToken cancellationToken = default)
|
||||||
/// </summary>
|
|
||||||
/// <param name="args">The arguments.</param>
|
|
||||||
/// <returns>The exit code from the executed command.</returns>
|
|
||||||
public async Task<int> RunAsync(IEnumerable<string> args)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -79,13 +68,14 @@ public sealed class CommandApp : ICommandApp
|
|||||||
cli.AddCommand<VersionCommand>(CliConstants.Commands.Version);
|
cli.AddCommand<VersionCommand>(CliConstants.Commands.Version);
|
||||||
cli.AddCommand<XmlDocCommand>(CliConstants.Commands.XmlDoc);
|
cli.AddCommand<XmlDocCommand>(CliConstants.Commands.XmlDoc);
|
||||||
cli.AddCommand<ExplainCommand>(CliConstants.Commands.Explain);
|
cli.AddCommand<ExplainCommand>(CliConstants.Commands.Explain);
|
||||||
|
cli.AddCommand<OpenCliGeneratorCommand>(CliConstants.Commands.OpenCli);
|
||||||
});
|
});
|
||||||
|
|
||||||
_executed = true;
|
_executed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await _executor
|
return await _executor
|
||||||
.Execute(_configurator, args)
|
.ExecuteAsync(_configurator, args, cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -108,6 +98,11 @@ public sealed class CommandApp : ICommandApp
|
|||||||
return _configurator.Settings.ExceptionHandler(ex, null);
|
return _configurator.Settings.ExceptionHandler(ex, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ex is OperationCanceledException)
|
||||||
|
{
|
||||||
|
return _configurator.Settings.CancellationExitCode;
|
||||||
|
}
|
||||||
|
|
||||||
// Render the exception.
|
// Render the exception.
|
||||||
var pretty = GetRenderableErrorMessage(ex);
|
var pretty = GetRenderableErrorMessage(ex);
|
||||||
if (pretty != null)
|
if (pretty != null)
|
||||||
|
|||||||
@@ -25,33 +25,22 @@ public sealed class CommandApp<TDefaultCommand> : ICommandApp
|
|||||||
_defaultCommandConfigurator = _app.SetDefaultCommand<TDefaultCommand>();
|
_defaultCommandConfigurator = _app.SetDefaultCommand<TDefaultCommand>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Configures the command line application.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configuration">The configuration.</param>
|
|
||||||
public void Configure(Action<IConfigurator> configuration)
|
public void Configure(Action<IConfigurator> configuration)
|
||||||
{
|
{
|
||||||
_app.Configure(configuration);
|
_app.Configure(configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Runs the command line application with specified arguments.
|
public int Run(IEnumerable<string> args, CancellationToken cancellationToken = default)
|
||||||
/// </summary>
|
|
||||||
/// <param name="args">The arguments.</param>
|
|
||||||
/// <returns>The exit code from the executed command.</returns>
|
|
||||||
public int Run(IEnumerable<string> args)
|
|
||||||
{
|
{
|
||||||
return _app.Run(args);
|
return _app.Run(args, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Runs the command line application with specified arguments.
|
public Task<int> RunAsync(IEnumerable<string> args, CancellationToken cancellationToken = default)
|
||||||
/// </summary>
|
|
||||||
/// <param name="args">The arguments.</param>
|
|
||||||
/// <returns>The exit code from the executed command.</returns>
|
|
||||||
public Task<int> RunAsync(IEnumerable<string> args)
|
|
||||||
{
|
{
|
||||||
return _app.RunAsync(args);
|
return _app.RunAsync(args, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Configurator GetConfigurator()
|
internal Configurator GetConfigurator()
|
||||||
|
|||||||
@@ -24,8 +24,9 @@ public abstract class Command<TSettings> : ICommand<TSettings>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The command context.</param>
|
/// <param name="context">The command context.</param>
|
||||||
/// <param name="settings">The settings.</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>
|
/// <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/>
|
/// <inheritdoc/>
|
||||||
ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings)
|
ValidationResult ICommand.Validate(CommandContext context, CommandSettings settings)
|
||||||
@@ -34,15 +35,15 @@ public abstract class Command<TSettings> : ICommand<TSettings>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <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.");
|
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/>
|
/// <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;
|
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>
|
/// <summary>
|
||||||
/// Configures case sensitivity.
|
/// Configures case sensitivity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -304,14 +322,14 @@ public static class ConfiguratorExtensions
|
|||||||
public static ICommandConfigurator AddDelegate(
|
public static ICommandConfigurator AddDelegate(
|
||||||
this IConfigurator configurator,
|
this IConfigurator configurator,
|
||||||
string name,
|
string name,
|
||||||
Func<CommandContext, int> func)
|
Func<CommandContext, CancellationToken, int> func)
|
||||||
{
|
{
|
||||||
if (configurator == null)
|
if (configurator == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(configurator));
|
throw new ArgumentNullException(nameof(configurator));
|
||||||
}
|
}
|
||||||
|
|
||||||
return configurator.AddDelegate<EmptyCommandSettings>(name, (c, _) => func(c));
|
return configurator.AddDelegate<EmptyCommandSettings>(name, (c, _, ct) => func(c, ct));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -324,14 +342,14 @@ public static class ConfiguratorExtensions
|
|||||||
public static ICommandConfigurator AddAsyncDelegate(
|
public static ICommandConfigurator AddAsyncDelegate(
|
||||||
this IConfigurator configurator,
|
this IConfigurator configurator,
|
||||||
string name,
|
string name,
|
||||||
Func<CommandContext, Task<int>> func)
|
Func<CommandContext, CancellationToken, Task<int>> func)
|
||||||
{
|
{
|
||||||
if (configurator == null)
|
if (configurator == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(configurator));
|
throw new ArgumentNullException(nameof(configurator));
|
||||||
}
|
}
|
||||||
|
|
||||||
return configurator.AddAsyncDelegate<EmptyCommandSettings>(name, (c, _) => func(c));
|
return configurator.AddAsyncDelegate<EmptyCommandSettings>(name, (c, _, ct) => func(c, ct));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -345,7 +363,7 @@ public static class ConfiguratorExtensions
|
|||||||
public static ICommandConfigurator AddDelegate<TSettings>(
|
public static ICommandConfigurator AddDelegate<TSettings>(
|
||||||
this IConfigurator<TSettings>? configurator,
|
this IConfigurator<TSettings>? configurator,
|
||||||
string name,
|
string name,
|
||||||
Func<CommandContext, int> func)
|
Func<CommandContext, CancellationToken, int> func)
|
||||||
where TSettings : CommandSettings
|
where TSettings : CommandSettings
|
||||||
{
|
{
|
||||||
if (typeof(TSettings).IsAbstract)
|
if (typeof(TSettings).IsAbstract)
|
||||||
@@ -358,7 +376,7 @@ public static class ConfiguratorExtensions
|
|||||||
throw new ArgumentNullException(nameof(configurator));
|
throw new ArgumentNullException(nameof(configurator));
|
||||||
}
|
}
|
||||||
|
|
||||||
return configurator.AddDelegate<TSettings>(name, (c, _) => func(c));
|
return configurator.AddDelegate<TSettings>(name, (c, _, ct) => func(c, ct));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -372,7 +390,7 @@ public static class ConfiguratorExtensions
|
|||||||
public static ICommandConfigurator AddAsyncDelegate<TSettings>(
|
public static ICommandConfigurator AddAsyncDelegate<TSettings>(
|
||||||
this IConfigurator<TSettings> configurator,
|
this IConfigurator<TSettings> configurator,
|
||||||
string name,
|
string name,
|
||||||
Func<CommandContext, Task<int>> func)
|
Func<CommandContext, CancellationToken, Task<int>> func)
|
||||||
where TSettings : CommandSettings
|
where TSettings : CommandSettings
|
||||||
{
|
{
|
||||||
if (configurator == null)
|
if (configurator == null)
|
||||||
@@ -380,7 +398,7 @@ public static class ConfiguratorExtensions
|
|||||||
throw new ArgumentNullException(nameof(configurator));
|
throw new ArgumentNullException(nameof(configurator));
|
||||||
}
|
}
|
||||||
|
|
||||||
return configurator.AddAsyncDelegate<TSettings>(name, (c, _) => func(c));
|
return configurator.AddAsyncDelegate<TSettings>(name, (c, _, ct) => func(c, ct));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public interface ICommand
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The command context.</param>
|
/// <param name="context">The command context.</param>
|
||||||
/// <param name="settings">The settings.</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>
|
/// <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.
|
/// Runs the command line application with specified arguments.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">The arguments.</param>
|
/// <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>
|
/// <returns>The exit code from the executed command.</returns>
|
||||||
int Run(IEnumerable<string> args);
|
int Run(IEnumerable<string> args, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs the command line application with specified arguments.
|
/// Runs the command line application with specified arguments.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">The arguments.</param>
|
/// <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>
|
/// <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>
|
/// </summary>
|
||||||
bool PropagateExceptions { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether or not examples should be validated.
|
/// Gets or sets a value indicating whether or not examples should be validated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ public interface ICommand<TSettings> : ICommandLimiter<TSettings>
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The command context.</param>
|
/// <param name="context">The command context.</param>
|
||||||
/// <param name="settings">The settings.</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>
|
/// <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="name">The name of the command.</param>
|
||||||
/// <param name="func">The delegate to execute as part of command execution.</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>
|
/// <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;
|
where TSettings : CommandSettings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -58,7 +58,7 @@ public interface IConfigurator
|
|||||||
/// <param name="name">The name of the command.</param>
|
/// <param name="name">The name of the command.</param>
|
||||||
/// <param name="func">The delegate to execute as part of command execution.</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>
|
/// <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;
|
where TSettings : CommandSettings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public interface IConfigurator<in TSettings>
|
|||||||
/// <param name="name">The name of the command.</param>
|
/// <param name="name">The name of the command.</param>
|
||||||
/// <param name="func">The delegate to execute as part of command execution.</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>
|
/// <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;
|
where TDerivedSettings : TSettings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -64,7 +64,7 @@ public interface IConfigurator<in TSettings>
|
|||||||
/// <param name="name">The name of the command.</param>
|
/// <param name="name">The name of the command.</param>
|
||||||
/// <param name="func">The delegate to execute as part of command execution.</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>
|
/// <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;
|
where TDerivedSettings : TSettings;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ internal sealed class CommandExecutor
|
|||||||
_registrar.Register(typeof(DefaultPairDeconstructor), typeof(DefaultPairDeconstructor));
|
_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;
|
CommandTreeParserResult parsedResult;
|
||||||
|
|
||||||
@@ -68,6 +68,13 @@ internal sealed class CommandExecutor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenCLI?
|
||||||
|
if (firstArgument.Equals(CliConstants.DumpHelpOpenCliOption, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// Replace all arguments with the opencligen command
|
||||||
|
arguments = ["cli", "opencli"];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse and map the model against the arguments.
|
// Parse and map the model against the arguments.
|
||||||
@@ -118,7 +125,7 @@ internal sealed class CommandExecutor
|
|||||||
leaf.Command.Data);
|
leaf.Command.Data);
|
||||||
|
|
||||||
// Execute the command tree.
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,12 +222,13 @@ internal sealed class CommandExecutor
|
|||||||
return (parsedResult, tokenizerResult);
|
return (parsedResult, tokenizerResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<int> Execute(
|
private static async Task<int> ExecuteAsync(
|
||||||
CommandTree leaf,
|
CommandTree leaf,
|
||||||
CommandTree tree,
|
CommandTree tree,
|
||||||
CommandContext context,
|
CommandContext context,
|
||||||
ITypeResolver resolver,
|
ITypeResolver resolver,
|
||||||
IConfiguration configuration)
|
IConfiguration configuration,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -249,7 +257,7 @@ internal sealed class CommandExecutor
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Execute the command.
|
// Execute the command.
|
||||||
var result = await command.Execute(context, settings);
|
var result = await command.ExecuteAsync(context, settings, cancellationToken);
|
||||||
foreach (var interceptor in interceptors)
|
foreach (var interceptor in interceptors)
|
||||||
{
|
{
|
||||||
interceptor.InterceptResult(context, settings, ref result);
|
interceptor.InterceptResult(context, settings, ref result);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Cli;
|
|||||||
|
|
||||||
[Description("Displays diagnostics about CLI configurations")]
|
[Description("Displays diagnostics about CLI configurations")]
|
||||||
[SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")]
|
[SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")]
|
||||||
internal sealed class ExplainCommand : Command<ExplainCommand.Settings>
|
internal sealed class ExplainCommand : Command<ExplainCommand.Settings>, IBuiltInCommand
|
||||||
{
|
{
|
||||||
private readonly CommandModel _commandModel;
|
private readonly CommandModel _commandModel;
|
||||||
private readonly IAnsiConsole _writer;
|
private readonly IAnsiConsole _writer;
|
||||||
@@ -27,7 +27,7 @@ internal sealed class ExplainCommand : Command<ExplainCommand.Settings>
|
|||||||
public bool IncludeHidden { get; set; }
|
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");
|
var tree = new Tree("CLI Configuration");
|
||||||
tree.AddNode(ValueMarkup("Application Name", _commandModel.ApplicationName, "no application name"));
|
tree.AddNode(ValueMarkup("Application Name", _commandModel.ApplicationName, "no application name"));
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Spectre.Console.Cli;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a built-in command.
|
||||||
|
/// Used as a marker interface.
|
||||||
|
/// </summary>
|
||||||
|
internal interface IBuiltInCommand : ICommand
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
using OpenCli;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Cli;
|
||||||
|
|
||||||
|
internal sealed class OpenCliGeneratorCommand : Command, IBuiltInCommand
|
||||||
|
{
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly CommandModel _model;
|
||||||
|
|
||||||
|
public OpenCliGeneratorCommand(IConfiguration configuration, CommandModel model)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_model = model ?? throw new ArgumentNullException(nameof(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Execute(CommandContext context, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var document = new OpenCliDocument
|
||||||
|
{
|
||||||
|
OpenCli = "0.1-draft",
|
||||||
|
Info = new OpenCliInfo
|
||||||
|
{
|
||||||
|
Title = ((ICommandModel)_model).ApplicationName, Version = _model.ApplicationVersion ?? "1.0",
|
||||||
|
},
|
||||||
|
Commands = CreateCommands(_model.Commands),
|
||||||
|
Arguments = CreateArguments(_model.DefaultCommand?.GetArguments()),
|
||||||
|
Options = CreateOptions(_model.DefaultCommand?.GetOptions()),
|
||||||
|
};
|
||||||
|
|
||||||
|
var writer = _configuration.Settings.Console.GetConsole();
|
||||||
|
writer.WriteLine(document.Write());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OpenCliCommand> CreateCommands(IList<CommandInfo> commands)
|
||||||
|
{
|
||||||
|
var result = new List<OpenCliCommand>();
|
||||||
|
|
||||||
|
foreach (var command in commands.OrderBy(o => o.Name, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (typeof(IBuiltInCommand).IsAssignableFrom(command.CommandType))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var openCliCommand = new OpenCliCommand
|
||||||
|
{
|
||||||
|
Name = command.Name,
|
||||||
|
Aliases =
|
||||||
|
[
|
||||||
|
..command.Aliases.OrderBy(str => str)
|
||||||
|
],
|
||||||
|
Commands = CreateCommands(command.Children),
|
||||||
|
Arguments = CreateArguments(command.GetArguments()),
|
||||||
|
Options = CreateOptions(command.GetOptions()),
|
||||||
|
Description = command.Description,
|
||||||
|
Hidden = command.IsHidden,
|
||||||
|
Examples = [..command.Examples.Select(example => string.Join(" ", example))],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Skip branches without commands
|
||||||
|
if (command.IsBranch && openCliCommand.Commands.Count == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Add(openCliCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OpenCliArgument> CreateArguments(IEnumerable<CommandArgument>? arguments)
|
||||||
|
{
|
||||||
|
var result = new List<OpenCliArgument>();
|
||||||
|
|
||||||
|
if (arguments == null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var argument in arguments.OrderBy(x => x.Position))
|
||||||
|
{
|
||||||
|
var metadata = default(List<OpenCliMetadata>);
|
||||||
|
if (argument.ParameterType != typeof(void) &&
|
||||||
|
argument.ParameterType != typeof(bool))
|
||||||
|
{
|
||||||
|
metadata =
|
||||||
|
[
|
||||||
|
new OpenCliMetadata { Name = "ClrType", Value = argument.ParameterType.ToCliTypeString(), },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Add(new OpenCliArgument
|
||||||
|
{
|
||||||
|
Name = argument.Value,
|
||||||
|
Required = argument.IsRequired,
|
||||||
|
Arity = new OpenCliArity
|
||||||
|
{
|
||||||
|
// TODO: Look into this
|
||||||
|
Minimum = 1,
|
||||||
|
Maximum = 1,
|
||||||
|
},
|
||||||
|
Description = argument.Description,
|
||||||
|
Hidden = argument.IsHidden,
|
||||||
|
Metadata = metadata,
|
||||||
|
AcceptedValues = null,
|
||||||
|
Group = null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OpenCliOption> CreateOptions(IEnumerable<CommandOption>? options)
|
||||||
|
{
|
||||||
|
var result = new List<OpenCliOption>();
|
||||||
|
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var option in options.OrderBy(o => o.GetOptionName(), StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var arguments = new List<OpenCliArgument>();
|
||||||
|
if (option.ParameterType != typeof(void) &&
|
||||||
|
option.ParameterType != typeof(bool))
|
||||||
|
{
|
||||||
|
arguments.Add(new OpenCliArgument
|
||||||
|
{
|
||||||
|
Name = option.ValueName ?? "VALUE",
|
||||||
|
Required = !option.ValueIsOptional,
|
||||||
|
Arity = new OpenCliArity
|
||||||
|
{
|
||||||
|
// TODO: Look into this
|
||||||
|
Minimum = option.ValueIsOptional
|
||||||
|
? 0
|
||||||
|
: 1,
|
||||||
|
Maximum = 1,
|
||||||
|
},
|
||||||
|
AcceptedValues = null,
|
||||||
|
Group = null,
|
||||||
|
Hidden = null,
|
||||||
|
Metadata =
|
||||||
|
[
|
||||||
|
new OpenCliMetadata
|
||||||
|
{
|
||||||
|
Name = "ClrType",
|
||||||
|
Value = option.ParameterType.ToCliTypeString(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var optionMetadata = default(List<OpenCliMetadata>);
|
||||||
|
if (arguments.Count == 0 && option.ParameterType != typeof(void) &&
|
||||||
|
option.ParameterType != typeof(bool))
|
||||||
|
{
|
||||||
|
optionMetadata =
|
||||||
|
[
|
||||||
|
new OpenCliMetadata { Name = "ClrType", Value = option.ParameterType.ToCliTypeString(), },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
var (optionName, optionAliases) = GetOptionNames(option);
|
||||||
|
result.Add(new OpenCliOption
|
||||||
|
{
|
||||||
|
Name = optionName,
|
||||||
|
Required = option.IsRequired,
|
||||||
|
Aliases = [..optionAliases.OrderBy(str => str)],
|
||||||
|
Arguments = arguments,
|
||||||
|
Description = option.Description,
|
||||||
|
Group = null,
|
||||||
|
Hidden = option.IsHidden,
|
||||||
|
Recursive = option.IsShadowed, // TODO: Is this correct?
|
||||||
|
Metadata = optionMetadata,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (string Name, HashSet<string> Aliases) GetOptionNames(CommandOption option)
|
||||||
|
{
|
||||||
|
var name = GetOptionName(option);
|
||||||
|
var aliases = new HashSet<string>();
|
||||||
|
|
||||||
|
if (option.LongNames.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var alias in option.LongNames.Skip(1))
|
||||||
|
{
|
||||||
|
aliases.Add("--" + alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var alias in option.ShortNames)
|
||||||
|
{
|
||||||
|
aliases.Add("-" + alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var alias in option.LongNames)
|
||||||
|
{
|
||||||
|
aliases.Add("--" + alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var alias in option.ShortNames.Skip(1))
|
||||||
|
{
|
||||||
|
aliases.Add("-" + alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (name, aliases);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetOptionName(CommandOption option)
|
||||||
|
{
|
||||||
|
return option.LongNames.Count > 0
|
||||||
|
? "--" + option.LongNames[0]
|
||||||
|
: "-" + option.ShortNames[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Cli;
|
|||||||
|
|
||||||
[Description("Displays the CLI library version")]
|
[Description("Displays the CLI library version")]
|
||||||
[SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")]
|
[SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")]
|
||||||
internal sealed class VersionCommand : Command<VersionCommand.Settings>
|
internal sealed class VersionCommand : Command, IBuiltInCommand
|
||||||
{
|
{
|
||||||
private readonly IAnsiConsole _writer;
|
private readonly IAnsiConsole _writer;
|
||||||
|
|
||||||
@@ -11,11 +11,7 @@ internal sealed class VersionCommand : Command<VersionCommand.Settings>
|
|||||||
_writer = configuration.Settings.Console.GetConsole();
|
_writer = configuration.Settings.Console.GetConsole();
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class Settings : CommandSettings
|
public override int Execute(CommandContext context, CancellationToken cancellationToken)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Execute(CommandContext context, Settings settings)
|
|
||||||
{
|
{
|
||||||
_writer.MarkupLine(
|
_writer.MarkupLine(
|
||||||
"[yellow]Spectre.Cli[/] version [aqua]{0}[/]",
|
"[yellow]Spectre.Cli[/] version [aqua]{0}[/]",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Cli;
|
|||||||
|
|
||||||
[Description("Generates an XML representation of the CLI configuration.")]
|
[Description("Generates an XML representation of the CLI configuration.")]
|
||||||
[SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")]
|
[SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")]
|
||||||
internal sealed class XmlDocCommand : Command<XmlDocCommand.Settings>
|
internal sealed class XmlDocCommand : Command, IBuiltInCommand
|
||||||
{
|
{
|
||||||
private readonly CommandModel _model;
|
private readonly CommandModel _model;
|
||||||
private readonly IAnsiConsole _writer;
|
private readonly IAnsiConsole _writer;
|
||||||
@@ -13,11 +13,7 @@ internal sealed class XmlDocCommand : Command<XmlDocCommand.Settings>
|
|||||||
_writer = configuration.Settings.Console.GetConsole();
|
_writer = configuration.Settings.Console.GetConsole();
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class Settings : CommandSettings
|
public override int Execute(CommandContext context, CancellationToken cancellationToken)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Execute(CommandContext context, Settings settings)
|
|
||||||
{
|
{
|
||||||
_writer.Write(Serialize(_model), Style.Plain);
|
_writer.Write(Serialize(_model), Style.Plain);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings
|
|||||||
public HelpProviderStyle? HelpProviderStyles { get; set; }
|
public HelpProviderStyle? HelpProviderStyles { get; set; }
|
||||||
public bool StrictParsing { get; set; }
|
public bool StrictParsing { get; set; }
|
||||||
public bool ConvertFlagsToRemainingArguments { get; set; }
|
public bool ConvertFlagsToRemainingArguments { get; set; }
|
||||||
|
public int CancellationExitCode { get; set; }
|
||||||
|
|
||||||
public ParsingMode ParsingMode =>
|
public ParsingMode ParsingMode =>
|
||||||
StrictParsing ? ParsingMode.Strict : ParsingMode.Relaxed;
|
StrictParsing ? ParsingMode.Strict : ParsingMode.Relaxed;
|
||||||
@@ -33,6 +34,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings
|
|||||||
TrimTrailingPeriod = true;
|
TrimTrailingPeriod = true;
|
||||||
HelpProviderStyles = HelpProviderStyle.Default;
|
HelpProviderStyles = HelpProviderStyle.Default;
|
||||||
ConvertFlagsToRemainingArguments = false;
|
ConvertFlagsToRemainingArguments = false;
|
||||||
|
CancellationExitCode = 130;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsTrue(Func<CommandAppSettings, bool> func, string environmentVariableName)
|
public bool IsTrue(Func<CommandAppSettings, bool> func, string environmentVariableName)
|
||||||
|
|||||||
@@ -56,19 +56,19 @@ internal sealed class Configurator : IUnsafeConfigurator, IConfigurator, IConfig
|
|||||||
return new CommandConfigurator(command);
|
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
|
where TSettings : CommandSettings
|
||||||
{
|
{
|
||||||
var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>(
|
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);
|
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
|
where TSettings : CommandSettings
|
||||||
{
|
{
|
||||||
var command = Commands.AddAndReturn(ConfiguredCommand.FromDelegate<TSettings>(
|
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);
|
return new CommandConfigurator(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,21 +46,21 @@ internal sealed class Configurator<TSettings> : IUnsafeBranchConfigurator, IConf
|
|||||||
return configurator;
|
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
|
where TDerivedSettings : TSettings
|
||||||
{
|
{
|
||||||
var command = ConfiguredCommand.FromDelegate<TDerivedSettings>(
|
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);
|
_command.Children.Add(command);
|
||||||
return new CommandConfigurator(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
|
where TDerivedSettings : TSettings
|
||||||
{
|
{
|
||||||
var command = ConfiguredCommand.FromDelegate<TDerivedSettings>(
|
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);
|
_command.Children.Add(command);
|
||||||
return new CommandConfigurator(command);
|
return new CommandConfigurator(command);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ internal sealed class ConfiguredCommand
|
|||||||
public object? Data { get; set; }
|
public object? Data { get; set; }
|
||||||
public Type? CommandType { get; }
|
public Type? CommandType { get; }
|
||||||
public Type SettingsType { 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 IsDefaultCommand { get; }
|
||||||
public bool IsHidden { get; set; }
|
public bool IsHidden { get; set; }
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ internal sealed class ConfiguredCommand
|
|||||||
string name,
|
string name,
|
||||||
Type? commandType,
|
Type? commandType,
|
||||||
Type settingsType,
|
Type settingsType,
|
||||||
Func<CommandContext, CommandSettings, Task<int>>? @delegate,
|
Func<CommandContext, CommandSettings, CancellationToken, Task<int>>? @delegate,
|
||||||
bool isDefaultCommand)
|
bool isDefaultCommand)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
@@ -60,7 +60,7 @@ internal sealed class ConfiguredCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ConfiguredCommand FromDelegate<TSettings>(
|
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
|
where TSettings : CommandSettings
|
||||||
{
|
{
|
||||||
return new ConfiguredCommand(name, null, typeof(TSettings), @delegate, false);
|
return new ConfiguredCommand(name, null, typeof(TSettings), @delegate, false);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ internal static class CliConstants
|
|||||||
public const string DefaultCommandName = "__default_command";
|
public const string DefaultCommandName = "__default_command";
|
||||||
public const string True = "true";
|
public const string True = "true";
|
||||||
public const string False = "false";
|
public const string False = "false";
|
||||||
|
public const string DumpHelpOpenCliOption = "--help-dump-opencli";
|
||||||
|
|
||||||
public static string[] AcceptedBooleanValues { get; } = new string[]
|
public static string[] AcceptedBooleanValues { get; } = new string[]
|
||||||
{
|
{
|
||||||
@@ -18,5 +19,6 @@ internal static class CliConstants
|
|||||||
public const string Version = "version";
|
public const string Version = "version";
|
||||||
public const string XmlDoc = "xmldoc";
|
public const string XmlDoc = "xmldoc";
|
||||||
public const string Explain = "explain";
|
public const string Explain = "explain";
|
||||||
|
public const string OpenCli = "opencli";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,16 +2,16 @@ namespace Spectre.Console.Cli;
|
|||||||
|
|
||||||
internal sealed class DelegateCommand : ICommand
|
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;
|
_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)
|
public ValidationResult Validate(CommandContext context, CommandSettings settings)
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#if NETSTANDARD2_0
|
||||||
|
namespace System.IO;
|
||||||
|
|
||||||
|
// Polyfills needed for OpenCli.
|
||||||
|
// This can be removed once me migrate over to the Polyfill library.
|
||||||
|
internal static class OpenCliExtensions
|
||||||
|
{
|
||||||
|
public static Task<string> ReadToEndAsync(this StreamReader reader, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return reader.ReadToEndAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -16,4 +16,19 @@ internal static class TypeExtensions
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Taken from https://github.com/dotnet/sdk/blob/main/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/TypeExtensions.cs#L15
|
||||||
|
// Licensed under MIT
|
||||||
|
public static string ToCliTypeString(this Type type)
|
||||||
|
{
|
||||||
|
var typeName = type.FullName ?? string.Empty;
|
||||||
|
if (!type.IsGenericType)
|
||||||
|
{
|
||||||
|
return typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
var genericTypeName = typeName.Substring(0, typeName.IndexOf('`'));
|
||||||
|
var genericTypes = string.Join(", ", type.GenericTypeArguments.Select(generic => generic.ToCliTypeString()));
|
||||||
|
return $"{genericTypeName}<{genericTypes}>";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ internal sealed class CommandInfo : ICommandContainer, ICommandInfo
|
|||||||
public object? Data { get; }
|
public object? Data { get; }
|
||||||
public Type? CommandType { get; }
|
public Type? CommandType { get; }
|
||||||
public Type SettingsType { 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 IsDefaultCommand { get; }
|
||||||
public CommandInfo? Parent { get; }
|
public CommandInfo? Parent { get; }
|
||||||
public IList<CommandInfo> Children { get; }
|
public IList<CommandInfo> Children { get; }
|
||||||
|
|||||||
@@ -2,6 +2,16 @@ namespace Spectre.Console.Cli;
|
|||||||
|
|
||||||
internal static class CommandInfoExtensions
|
internal static class CommandInfoExtensions
|
||||||
{
|
{
|
||||||
|
public static IEnumerable<CommandArgument>? GetArguments(this CommandInfo? command)
|
||||||
|
{
|
||||||
|
return command?.Parameters.OfType<CommandArgument>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<CommandOption>? GetOptions(this CommandInfo? command)
|
||||||
|
{
|
||||||
|
return command?.Parameters.OfType<CommandOption>();
|
||||||
|
}
|
||||||
|
|
||||||
public static bool HaveParentWithOption(this CommandInfo command, CommandOption option)
|
public static bool HaveParentWithOption(this CommandInfo command, CommandOption option)
|
||||||
{
|
{
|
||||||
var parent = command?.Parent;
|
var parent = command?.Parent;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ internal abstract class CommandParameter : ICommandParameterInfo, ICommandParame
|
|||||||
DefaultValue = defaultValue;
|
DefaultValue = defaultValue;
|
||||||
PairDeconstructor = deconstructor;
|
PairDeconstructor = deconstructor;
|
||||||
ValueProvider = valueProvider;
|
ValueProvider = valueProvider;
|
||||||
Validators = new List<ParameterValidationAttribute>(validators ?? Array.Empty<ParameterValidationAttribute>());
|
Validators = new List<ParameterValidationAttribute>(validators ?? []);
|
||||||
IsRequired = required;
|
IsRequired = required;
|
||||||
IsHidden = isHidden;
|
IsHidden = isHidden;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ global using System.IO;
|
|||||||
global using System.Linq;
|
global using System.Linq;
|
||||||
global using System.Reflection;
|
global using System.Reflection;
|
||||||
global using System.Text;
|
global using System.Text;
|
||||||
|
global using System.Threading;
|
||||||
global using System.Threading.Tasks;
|
global using System.Threading.Tasks;
|
||||||
global using System.Xml;
|
global using System.Xml;
|
||||||
global using Spectre.Console.Cli.Help;
|
global using Spectre.Console.Cli.Help;
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net9.0;net8.0;netstandard2.0</TargetFrameworks>
|
<TargetFrameworks>net9.0;net8.0;netstandard2.0</TargetFrameworks>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup>
|
|
||||||
<IsAotCompatible Condition="'$(TargetFramework)' != 'netstandard2.0'">false</IsAotCompatible>
|
<IsAotCompatible Condition="'$(TargetFramework)' != 'netstandard2.0'">false</IsAotCompatible>
|
||||||
<IsTrimmable>false</IsTrimmable>
|
<IsTrimmable>false</IsTrimmable>
|
||||||
|
<DefineConstants>$(DefineConstants);OPENCLI_VISIBILITY_INTERNAL</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Label="REMOVE THIS">
|
<ItemGroup Label="REMOVE THIS">
|
||||||
<InternalsVisibleTo Include="Spectre.Console.Cli.Tests"/>
|
<InternalsVisibleTo Include="Spectre.Console.Cli.Tests"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Label="Dependencies">
|
<ItemGroup Label="Dependencies">
|
||||||
|
<PackageReference Include="OpenCli.Sources" />
|
||||||
<PackageReference Include="PolySharp">
|
<PackageReference Include="PolySharp">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public sealed class CommandAppTester
|
|||||||
{
|
{
|
||||||
try
|
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.");
|
throw new InvalidOperationException("Expected an exception to be thrown, but there was none.");
|
||||||
}
|
}
|
||||||
catch (T ex)
|
catch (T ex)
|
||||||
@@ -129,53 +129,21 @@ public sealed class CommandAppTester
|
|||||||
/// <returns>The result.</returns>
|
/// <returns>The result.</returns>
|
||||||
public CommandAppResult Run(params string[] args)
|
public CommandAppResult Run(params string[] args)
|
||||||
{
|
{
|
||||||
return Run(args, Console);
|
return RunAsync(args, Console).GetAwaiter().GetResult();
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs the command application asynchronously.
|
/// Runs the command application asynchronously.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">The arguments.</param>
|
/// <param name="args">The arguments.</param>
|
||||||
|
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
|
||||||
/// <returns>The result.</returns>
|
/// <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;
|
CommandContext? context = null;
|
||||||
CommandSettings? settings = null;
|
CommandSettings? settings = null;
|
||||||
@@ -200,7 +168,7 @@ public sealed class CommandAppTester
|
|||||||
settings = s;
|
settings = s;
|
||||||
})));
|
})));
|
||||||
|
|
||||||
var result = await app.RunAsync(args);
|
var result = await app.RunAsync(args, cancellationToken);
|
||||||
|
|
||||||
var output = console.Output.NormalizeLineEndings();
|
var output = console.Output.NormalizeLineEndings();
|
||||||
output = TestSettings.TrimConsoleOutput ? output.TrimLines().Trim() : output;
|
output = TestSettings.TrimConsoleOutput ? output.TrimLines().Trim() : output;
|
||||||
|
|||||||
@@ -3,16 +3,17 @@ namespace Spectre.Console.Tests;
|
|||||||
public static class Constants
|
public static class Constants
|
||||||
{
|
{
|
||||||
public static string[] VersionCommand { get; } =
|
public static string[] VersionCommand { get; } =
|
||||||
new[]
|
[
|
||||||
{
|
|
||||||
CliConstants.Commands.Branch,
|
CliConstants.Commands.Branch,
|
||||||
CliConstants.Commands.Version,
|
CliConstants.Commands.Version
|
||||||
};
|
];
|
||||||
|
|
||||||
public static string[] XmlDocCommand { get; } =
|
public static string[] XmlDocCommand { get; } =
|
||||||
new[]
|
[
|
||||||
{
|
|
||||||
CliConstants.Commands.Branch,
|
CliConstants.Commands.Branch,
|
||||||
CliConstants.Commands.XmlDoc,
|
CliConstants.Commands.XmlDoc
|
||||||
};
|
];
|
||||||
|
|
||||||
|
public static string[] OpenCliOption { get; } =
|
||||||
|
[CliConstants.DumpHelpOpenCliOption];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ public sealed class AsynchronousCommand : AsyncCommand<AsynchronousCommandSettin
|
|||||||
_console = console;
|
_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
|
// Simulate a long running asynchronous task
|
||||||
await Task.Delay(200);
|
await Task.Delay(200, cancellationToken);
|
||||||
|
|
||||||
if (settings.ThrowException)
|
if (settings.ThrowException)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data;
|
|||||||
|
|
||||||
public class CatCommand : AnimalCommand<CatSettings>
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class DogCommand : AnimalCommand<DogSettings>
|
|||||||
return base.Validate(context, settings);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public sealed class DumpRemainingCommand : Command<EmptyCommandSettings>
|
|||||||
_console = console;
|
_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)
|
if (context.Remaining.Raw.Count > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data;
|
|||||||
|
|
||||||
public sealed class EmptyCommand : Command<EmptyCommandSettings>
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data;
|
|||||||
public sealed class GenericCommand<TSettings> : Command<TSettings>
|
public sealed class GenericCommand<TSettings> : Command<TSettings>
|
||||||
where TSettings : CommandSettings
|
where TSettings : CommandSettings
|
||||||
{
|
{
|
||||||
public override int Execute(CommandContext context, TSettings settings)
|
public override int Execute(CommandContext context, TSettings settings, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data;
|
|||||||
[Description("The giraffe command.")]
|
[Description("The giraffe command.")]
|
||||||
public sealed class GiraffeCommand : Command<GiraffeSettings>
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public class GreeterCommand : Command<OptionalArgumentWithDefaultValueSettings>
|
|||||||
_console = console;
|
_console = console;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings)
|
public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_console.WriteLine(settings.Greeting);
|
_console.WriteLine(settings.Greeting);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data;
|
|||||||
|
|
||||||
public sealed class HiddenOptionsCommand : Command<HiddenOptionSettings>
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data;
|
|||||||
[Description("The horse command.")]
|
[Description("The horse command.")]
|
||||||
public class HorseCommand : AnimalCommand<HorseSettings>
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data;
|
|||||||
|
|
||||||
public sealed class InvalidCommand : Command<InvalidSettings>
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data;
|
|||||||
[Description("The lion command.")]
|
[Description("The lion command.")]
|
||||||
public class LionCommand : AnimalCommand<LionSettings>
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ public sealed class NoDescriptionCommand : Command<EmptyCommandSettings>
|
|||||||
[CommandOption("-f|--foo <VALUE>")]
|
[CommandOption("-f|--foo <VALUE>")]
|
||||||
public int Foo { get; set; }
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data;
|
|||||||
|
|
||||||
public class OptionVectorCommand : Command<OptionVectorSettings>
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace Spectre.Console.Tests.Data;
|
|||||||
|
|
||||||
public sealed class ThrowingCommand : Command<ThrowingCommandSettings>
|
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?");
|
throw new InvalidOperationException("W00t?");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace Spectre.Console.Tests.Data;
|
|||||||
[Description("The turtle command.")]
|
[Description("The turtle command.")]
|
||||||
public class TurtleCommand : AnimalCommand<TurtleSettings>
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public sealed class VersionCommand : Command<VersionSettings>
|
|||||||
_console = console;
|
_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}");
|
_console.WriteLine($"VersionCommand ran, Version: {settings.Version ?? string.Empty}");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
{
|
||||||
|
"opencli": "0.1-draft",
|
||||||
|
"info": {
|
||||||
|
"title": "my-app",
|
||||||
|
"version": "1.2.3"
|
||||||
|
},
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"name": "animals",
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"name": "cat",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"name": "--agility",
|
||||||
|
"required": false,
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "VALUE",
|
||||||
|
"required": true,
|
||||||
|
"arity": {
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 1
|
||||||
|
},
|
||||||
|
"metadata": [
|
||||||
|
{
|
||||||
|
"name": "ClrType",
|
||||||
|
"value": "System.Int32"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The agility between 0 and 100.",
|
||||||
|
"recursive": false,
|
||||||
|
"hidden": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "--alive",
|
||||||
|
"required": false,
|
||||||
|
"aliases": [
|
||||||
|
"--not-dead",
|
||||||
|
"-a"
|
||||||
|
],
|
||||||
|
"description": "Indicates whether or not the animal is alive.",
|
||||||
|
"recursive": false,
|
||||||
|
"hidden": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "--name",
|
||||||
|
"required": false,
|
||||||
|
"aliases": [
|
||||||
|
"--pet-name",
|
||||||
|
"-n",
|
||||||
|
"-p"
|
||||||
|
],
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "VALUE",
|
||||||
|
"required": true,
|
||||||
|
"arity": {
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 1
|
||||||
|
},
|
||||||
|
"metadata": [
|
||||||
|
{
|
||||||
|
"name": "ClrType",
|
||||||
|
"value": "System.String"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"recursive": false,
|
||||||
|
"hidden": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "LEGS",
|
||||||
|
"required": false,
|
||||||
|
"arity": {
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 1
|
||||||
|
},
|
||||||
|
"description": "The number of legs.",
|
||||||
|
"hidden": false,
|
||||||
|
"metadata": [
|
||||||
|
{
|
||||||
|
"name": "ClrType",
|
||||||
|
"value": "System.Int32"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hidden": false,
|
||||||
|
"examples": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dog",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"name": "--alive",
|
||||||
|
"required": false,
|
||||||
|
"aliases": [
|
||||||
|
"--not-dead",
|
||||||
|
"-a"
|
||||||
|
],
|
||||||
|
"description": "Indicates whether or not the animal is alive.",
|
||||||
|
"recursive": false,
|
||||||
|
"hidden": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "--good-boy",
|
||||||
|
"required": false,
|
||||||
|
"aliases": [
|
||||||
|
"-g"
|
||||||
|
],
|
||||||
|
"recursive": false,
|
||||||
|
"hidden": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "--name",
|
||||||
|
"required": false,
|
||||||
|
"aliases": [
|
||||||
|
"--pet-name",
|
||||||
|
"-n",
|
||||||
|
"-p"
|
||||||
|
],
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "VALUE",
|
||||||
|
"required": true,
|
||||||
|
"arity": {
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 1
|
||||||
|
},
|
||||||
|
"metadata": [
|
||||||
|
{
|
||||||
|
"name": "ClrType",
|
||||||
|
"value": "System.String"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"recursive": false,
|
||||||
|
"hidden": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "LEGS",
|
||||||
|
"required": false,
|
||||||
|
"arity": {
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 1
|
||||||
|
},
|
||||||
|
"description": "The number of legs.",
|
||||||
|
"hidden": false,
|
||||||
|
"metadata": [
|
||||||
|
{
|
||||||
|
"name": "ClrType",
|
||||||
|
"value": "System.Int32"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AGE",
|
||||||
|
"required": true,
|
||||||
|
"arity": {
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 1
|
||||||
|
},
|
||||||
|
"hidden": false,
|
||||||
|
"metadata": [
|
||||||
|
{
|
||||||
|
"name": "ClrType",
|
||||||
|
"value": "System.Int32"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The dog command.",
|
||||||
|
"hidden": false,
|
||||||
|
"examples": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hidden": false,
|
||||||
|
"examples": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ global using System.Diagnostics.CodeAnalysis;
|
|||||||
global using System.Globalization;
|
global using System.Globalization;
|
||||||
global using System.Linq;
|
global using System.Linq;
|
||||||
global using System.Runtime.CompilerServices;
|
global using System.Runtime.CompilerServices;
|
||||||
|
global using System.Threading;
|
||||||
global using System.Threading.Tasks;
|
global using System.Threading.Tasks;
|
||||||
global using Shouldly;
|
global using Shouldly;
|
||||||
global using Spectre.Console.Cli;
|
global using Spectre.Console.Cli;
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public sealed partial class CommandAppTests
|
|||||||
});
|
});
|
||||||
|
|
||||||
// When
|
// When
|
||||||
var result = await Record.ExceptionAsync(async () =>
|
var exception = await Record.ExceptionAsync(async () =>
|
||||||
await app.RunAsync(new[]
|
await app.RunAsync(new[]
|
||||||
{
|
{
|
||||||
"--ThrowException",
|
"--ThrowException",
|
||||||
@@ -61,10 +61,64 @@ public sealed partial class CommandAppTests
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
result.ShouldBeOfType<Exception>().And(ex =>
|
exception.ShouldBeOfType<Exception>().And(ex =>
|
||||||
{
|
{
|
||||||
ex.Message.ShouldBe("Throwing exception asynchronously");
|
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 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 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]
|
[Fact]
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public sealed partial class CommandAppTests
|
|||||||
_dep = dep;
|
_dep = dep;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int Execute(CommandContext context, CustomInheritedCommandSettings settings)
|
public override int Execute(CommandContext context, CustomInheritedCommandSettings settings, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return 0;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
namespace Spectre.Console.Tests.Unit.Cli;
|
||||||
|
|
||||||
|
public sealed partial class CommandAppTests
|
||||||
|
{
|
||||||
|
[ExpectationPath("OpenCli")]
|
||||||
|
public sealed partial class OpenCli
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
[Expectation("Generate")]
|
||||||
|
public Task Should_Output_OpenCli_Description()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new CommandAppTester();
|
||||||
|
fixture.Configure(config =>
|
||||||
|
{
|
||||||
|
config.SetApplicationName("my-app");
|
||||||
|
config.SetApplicationVersion("1.2.3");
|
||||||
|
|
||||||
|
config.AddBranch("animals", animals =>
|
||||||
|
{
|
||||||
|
animals.AddCommand<DogCommand>("dog");
|
||||||
|
animals.AddCommand<CatCommand>("cat");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = fixture.Run(Constants.OpenCliOption);
|
||||||
|
|
||||||
|
// Then
|
||||||
|
return Verifier.Verify(result.Output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1117,7 +1117,7 @@ public sealed partial class CommandAppTests
|
|||||||
{
|
{
|
||||||
config.PropagateExceptions();
|
config.PropagateExceptions();
|
||||||
config.AddDelegate<DogSettings>(
|
config.AddDelegate<DogSettings>(
|
||||||
"foo", (context, settings) =>
|
"foo", (context, settings, _) =>
|
||||||
{
|
{
|
||||||
dog = settings;
|
dog = settings;
|
||||||
data = (int)context.Data;
|
data = (int)context.Data;
|
||||||
@@ -1145,7 +1145,7 @@ public sealed partial class CommandAppTests
|
|||||||
{
|
{
|
||||||
cfg.AddBranch("a", d =>
|
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();
|
var app = new CommandAppTester();
|
||||||
app.Configure(cfg =>
|
app.Configure(cfg =>
|
||||||
{
|
{
|
||||||
cfg.AddDelegate("a", _ => 0);
|
cfg.AddDelegate("a", (_, _) => 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// When
|
// When
|
||||||
@@ -1189,7 +1189,7 @@ public sealed partial class CommandAppTests
|
|||||||
{
|
{
|
||||||
config.PropagateExceptions();
|
config.PropagateExceptions();
|
||||||
config.AddAsyncDelegate<DogSettings>(
|
config.AddAsyncDelegate<DogSettings>(
|
||||||
"foo", (context, settings) =>
|
"foo", (context, settings, _) =>
|
||||||
{
|
{
|
||||||
dog = settings;
|
dog = settings;
|
||||||
data = (int)context.Data;
|
data = (int)context.Data;
|
||||||
@@ -1222,7 +1222,7 @@ public sealed partial class CommandAppTests
|
|||||||
config.AddBranch<AnimalSettings>("foo", foo =>
|
config.AddBranch<AnimalSettings>("foo", foo =>
|
||||||
{
|
{
|
||||||
foo.AddDelegate<DogSettings>(
|
foo.AddDelegate<DogSettings>(
|
||||||
"bar", (context, settings) =>
|
"bar", (context, settings, _) =>
|
||||||
{
|
{
|
||||||
dog = settings;
|
dog = settings;
|
||||||
data = (int)context.Data;
|
data = (int)context.Data;
|
||||||
@@ -1256,7 +1256,7 @@ public sealed partial class CommandAppTests
|
|||||||
config.AddBranch<AnimalSettings>("foo", foo =>
|
config.AddBranch<AnimalSettings>("foo", foo =>
|
||||||
{
|
{
|
||||||
foo.AddAsyncDelegate<DogSettings>(
|
foo.AddAsyncDelegate<DogSettings>(
|
||||||
"bar", (context, settings) =>
|
"bar", (context, settings, _) =>
|
||||||
{
|
{
|
||||||
dog = settings;
|
dog = settings;
|
||||||
data = (int)context.Data;
|
data = (int)context.Data;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public sealed class CommandAppTesterTests
|
|||||||
_console = console;
|
_console = console;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings)
|
public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_console.Write(settings.Greeting);
|
_console.Write(settings.Greeting);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public sealed class InteractiveCommandTests
|
|||||||
_console = console;
|
_console = console;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int Execute(CommandContext context)
|
public override int Execute(CommandContext context, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var fruits = _console.Prompt(
|
var fruits = _console.Prompt(
|
||||||
new MultiSelectionPrompt<string>()
|
new MultiSelectionPrompt<string>()
|
||||||
|
|||||||
Reference in New Issue
Block a user