mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa3094ee54 | ||
|
|
712580e3d7 | ||
|
|
c08102f85f | ||
|
|
5e684c8b36 | ||
|
|
300ae70564 | ||
|
|
76f0c77f1e | ||
|
|
0f7cea4ed1 | ||
|
|
32ee0b2bd6 | ||
|
|
4ff1e1d3e1 | ||
|
|
8e96d2701d | ||
|
|
8e307df231 | ||
|
|
ff38f4916a | ||
|
|
7cbbb220b4 | ||
|
|
ae2d4299f0 | ||
|
|
21bc69d116 | ||
|
|
05a70175cc | ||
|
|
33ec2eb3a0 | ||
|
|
f6ef6cd4c0 | ||
|
|
a9ef693dc1 | ||
|
|
98bbd666dc | ||
|
|
4e7ed830f8 | ||
|
|
ef87ff76fc | ||
|
|
2feeb21270 | ||
|
|
9990387cfa | ||
|
|
bc1bdca7c6 | ||
|
|
2a992d37df | ||
|
|
15c87aecbb | ||
|
|
10a46451ac | ||
|
|
e4c6a4174b | ||
|
|
4c65f7bbee | ||
|
|
5f21de0df5 | ||
|
|
9b01b67d98 | ||
|
|
4508f5e211 | ||
|
|
f0cbc46df4 | ||
|
|
6c96e9e173 |
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,3 +0,0 @@
|
||||
github: Tyrrrz
|
||||
patreon: Tyrrrz
|
||||
custom: ['buymeacoffee.com/Tyrrrz', 'tyrrrz.me/donate']
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💬 Discord server
|
||||
url: https://discord.gg/2SUWKFnHSm
|
||||
about: Chat with the project community.
|
||||
- name: 🗨 Discussions
|
||||
url: https://github.com/Tyrrrz/CliFx/discussions/new
|
||||
about: Ask and answer questions.
|
||||
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,17 +0,0 @@
|
||||
<!--
|
||||
|
||||
**Important:**
|
||||
|
||||
Please make sure that there is an existing issue that describes the problem solved by your pull request. If there isn't one, consider creating it first.
|
||||
An open issue offers a good place to iron out requirements, discuss possible solutions, and ask questions.
|
||||
|
||||
Remember to also:
|
||||
|
||||
- Keep your pull request focused and as small as possible. If you want to contribute multiple unrelated changes, please create separate pull requests for them.
|
||||
- Follow the coding style and conventions already established by the project. When in doubt about which style to use, ask in the comments to your pull request.
|
||||
- If you want to start a discussion regarding a specific change you've made, add a review comment to your own code. This can be used to highlight something important or to seek further input from others.
|
||||
|
||||
-->
|
||||
|
||||
<!-- Please specify the issue addressed by this pull request -->
|
||||
Closes #ISSUE_NUMBER
|
||||
25
.github/workflows/CD.yml
vendored
25
.github/workflows/CD.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: CD
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
- name: Install .NET
|
||||
uses: actions/setup-dotnet@v1.7.2
|
||||
with:
|
||||
dotnet-version: 5.0.x
|
||||
|
||||
- name: Pack
|
||||
run: dotnet pack CliFx --configuration Release
|
||||
|
||||
- name: Deploy
|
||||
run: dotnet nuget push CliFx/bin/Release/*.nupkg -s nuget.org -k ${{ secrets.NUGET_TOKEN }}
|
||||
28
.github/workflows/CI.yml
vendored
28
.github/workflows/CI.yml
vendored
@@ -1,28 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
|
||||
- name: Install .NET
|
||||
uses: actions/setup-dotnet@v1.7.2
|
||||
with:
|
||||
dotnet-version: 5.0.x
|
||||
|
||||
- name: Build & test
|
||||
run: dotnet test --configuration Release --logger GitHubActions
|
||||
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v1.0.5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
11
.github/workflows/main.yml
vendored
Normal file
11
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
name: main
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
main:
|
||||
uses: Tyrrrz/.github/.github/workflows/NuGet.yml@master
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
15
Changelog.md
15
Changelog.md
@@ -1,3 +1,18 @@
|
||||
### v2.2.1 (16-Jan-2022)
|
||||
|
||||
- Fixed an issue which caused help text to not show default values for optional parameters. (Thanks [@AliReZa Sabouri](https://github.com/alirezanet))
|
||||
|
||||
### v2.2 (11-Jan-2022)
|
||||
|
||||
- Added support for optional parameters. A parameter can be marked as optional by setting `IsRequired = false` on the attribute. Only one parameter is allowed to be optional and such parameter must be the last in order. (Thanks [@AliReZa Sabouri](https://github.com/alirezanet))
|
||||
- Fixed an issue where parameters and options bound to properties implemented as default interface members were not working correctly. (Thanks [@AliReZa Sabouri](https://github.com/alirezanet))
|
||||
|
||||
### v2.1 (04-Jan-2022)
|
||||
|
||||
- Added `IConsole.Clear()` with corresponding implementations in `SystemConsole`, `FakeConsole`, and `FakeInMemoryConsole`. (Thanks [@Alex Rosenfeld](https://github.com/alexrosenfeld10))
|
||||
- Added `IConsole.ReadKey()` with corresponding implementations in `SystemConsole`, `FakeConsole`, and `FakeInMemoryConsole`. (Thanks [@Alex Rosenfeld](https://github.com/alexrosenfeld10))
|
||||
- Fixed an issue that caused parameters to appear out of order in the usage format section of the help text. (Thanks [@David Fallah](https://github.com/TAGC))
|
||||
|
||||
### v2.0.6 (17-Jul-2021)
|
||||
|
||||
- Fixed an issue where an exception thrown via reflection during parameter or option binding resulted in `Exception has been thrown by the target of an invocation` error instead of a more useful message. Such exceptions will now be unwrapped to provide better user experience.
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<CollectCoverage>true</CollectCoverage>
|
||||
<CoverletOutputFormat>opencover</CoverletOutputFormat>
|
||||
</PropertyGroup>
|
||||
@@ -13,14 +11,14 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Basic.Reference.Assemblies" Version="1.1.2" />
|
||||
<PackageReference Include="Basic.Reference.Assemblies" Version="1.2.4" />
|
||||
<PackageReference Include="GitHubActionsTestLogger" Version="1.2.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.3.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="all" />
|
||||
<PackageReference Include="coverlet.msbuild" Version="3.0.3" PrivateAssets="all" />
|
||||
<PackageReference Include="coverlet.msbuild" Version="3.1.0" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class CommandMustBeAnnotatedAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new CommandMustBeAnnotatedAnalyzer();
|
||||
@@ -69,4 +69,3 @@ public class Foo
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class CommandMustImplementInterfaceAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new CommandMustImplementInterfaceAnalyzer();
|
||||
@@ -55,4 +55,3 @@ public class Foo
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using FluentAssertions;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class GeneralSpecs
|
||||
{
|
||||
[Fact]
|
||||
@@ -28,4 +28,3 @@ namespace CliFx.Analyzers.Tests
|
||||
diagnosticIds.Should().OnlyHaveUniqueItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustBeInsideCommandAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustBeInsideCommandAnalyzer();
|
||||
@@ -77,4 +77,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustHaveNameOrShortNameAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveNameOrShortNameAnalyzer();
|
||||
@@ -83,4 +83,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustHaveUniqueNameAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveUniqueNameAnalyzer();
|
||||
@@ -89,4 +89,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustHaveUniqueShortNameAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveUniqueShortNameAnalyzer();
|
||||
@@ -111,4 +111,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustHaveValidConverterAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidConverterAnalyzer();
|
||||
@@ -93,4 +93,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustHaveValidNameAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidNameAnalyzer();
|
||||
@@ -102,4 +102,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustHaveValidShortNameAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidShortNameAnalyzer();
|
||||
@@ -83,4 +83,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustHaveValidValidatorsAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidValidatorsAnalyzer();
|
||||
@@ -93,4 +93,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustBeInsideCommandAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeInsideCommandAnalyzer();
|
||||
@@ -77,4 +77,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using CliFx.Analyzers.Tests.Utils;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustBeLastIfNonRequiredAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeLastIfNonRequiredAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_a_non_required_parameter_is_not_the_last_in_order()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code = @"
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
[CommandParameter(0, Name = ""foo"", IsRequired = false)]
|
||||
public string Foo { get; set; }
|
||||
|
||||
[CommandParameter(1, Name = ""bar"")]
|
||||
public string Bar { get; set; }
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}";
|
||||
|
||||
// Act & assert
|
||||
Analyzer.Should().ProduceDiagnostics(code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_does_not_report_an_error_if_a_non_required_parameter_is_the_last_in_order()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code = @"
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
[CommandParameter(0, Name = ""foo"")]
|
||||
public string Foo { get; set; }
|
||||
|
||||
[CommandParameter(1, Name = ""bar"", IsRequired = false)]
|
||||
public string Bar { get; set; }
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}";
|
||||
|
||||
// Act & assert
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_does_not_report_an_error_if_no_non_required_parameters_are_defined()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code = @"
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
[CommandParameter(0, Name = ""foo"")]
|
||||
public string Foo { get; set; }
|
||||
|
||||
[CommandParameter(1, Name = ""bar"", IsRequired = true)]
|
||||
public string Bar { get; set; }
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}";
|
||||
|
||||
// Act & assert
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code = @"
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
public string Foo { get; set; }
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}";
|
||||
|
||||
// Act & assert
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,14 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustBeLastIfNonScalarAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeLastIfNonScalarAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_a_non_scalar_parameter_is_not_last_in_order()
|
||||
public void Analyzer_reports_an_error_if_a_non_scalar_parameter_is_not_the_last_in_order()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
@@ -31,7 +31,7 @@ public class MyCommand : ICommand
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_does_not_report_an_error_if_a_non_scalar_parameter_is_last_in_order()
|
||||
public void Analyzer_does_not_report_an_error_if_a_non_scalar_parameter_is_the_last_in_order()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
@@ -92,4 +92,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using CliFx.Analyzers.Tests.Utils;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustBeSingleIfNonRequiredAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeSingleIfNonRequiredAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_more_than_one_non_required_parameters_are_defined()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code = @"
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
[CommandParameter(0, IsRequired = false)]
|
||||
public string Foo { get; set; }
|
||||
|
||||
[CommandParameter(1, IsRequired = false)]
|
||||
public string Bar { get; set; }
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}";
|
||||
|
||||
// Act & assert
|
||||
Analyzer.Should().ProduceDiagnostics(code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_does_not_report_an_error_if_only_one_non_required_parameter_is_defined()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code = @"
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
[CommandParameter(0)]
|
||||
public string Foo { get; set; }
|
||||
|
||||
[CommandParameter(1, IsRequired = false)]
|
||||
public string Bar { get; set; }
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}";
|
||||
|
||||
// Act & assert
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_does_not_report_an_error_if_no_non_required_parameters_are_defined()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code = @"
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
[CommandParameter(0)]
|
||||
public string Foo { get; set; }
|
||||
|
||||
[CommandParameter(1, IsRequired = true)]
|
||||
public string Bar { get; set; }
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}";
|
||||
|
||||
// Act & assert
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code = @"
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
public string Foo { get; set; }
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}";
|
||||
|
||||
// Act & assert
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustBeSingleIfNonScalarAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeSingleIfNonScalarAnalyzer();
|
||||
@@ -92,4 +92,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustHaveUniqueNameAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveUniqueNameAnalyzer();
|
||||
@@ -70,4 +70,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustHaveUniqueOrderAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveUniqueOrderAnalyzer();
|
||||
@@ -70,4 +70,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustHaveValidConverterAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveValidConverterAnalyzer();
|
||||
@@ -93,4 +93,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustHaveValidValidatorsAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveValidValidatorsAnalyzer();
|
||||
@@ -93,4 +93,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace CliFx.Analyzers.Tests
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class SystemConsoleShouldBeAvoidedAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new SystemConsoleShouldBeAvoidedAnalyzer();
|
||||
@@ -124,4 +124,3 @@ public class MyCommand : ICommand
|
||||
Analyzer.Should().NotProduceDiagnostics(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@ using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace CliFx.Analyzers.Tests.Utils
|
||||
{
|
||||
namespace CliFx.Analyzers.Tests.Utils;
|
||||
|
||||
internal class AnalyzerAssertions : ReferenceTypeAssertions<DiagnosticAnalyzer, AnalyzerAssertions>
|
||||
{
|
||||
protected override string Identifier { get; } = "analyzer";
|
||||
@@ -165,4 +165,3 @@ namespace CliFx.Analyzers.Tests.Utils
|
||||
{
|
||||
public static AnalyzerAssertions Should(this DiagnosticAnalyzer analyzer) => new(analyzer);
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ using CliFx.Analyzers.Utils.Extensions;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
public abstract class AnalyzerBase : DiagnosticAnalyzer
|
||||
{
|
||||
public DiagnosticDescriptor SupportedDiagnostic { get; }
|
||||
@@ -37,4 +37,3 @@ namespace CliFx.Analyzers
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.4.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class CommandMustBeAnnotatedAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -51,4 +51,3 @@ namespace CliFx.Analyzers
|
||||
context.HandleClassDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class CommandMustImplementInterfaceAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -45,4 +45,3 @@ namespace CliFx.Analyzers
|
||||
context.HandleClassDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ using Microsoft.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using CliFx.Analyzers.Utils.Extensions;
|
||||
|
||||
namespace CliFx.Analyzers.ObjectModel
|
||||
{
|
||||
namespace CliFx.Analyzers.ObjectModel;
|
||||
|
||||
internal partial class CommandOptionSymbol
|
||||
{
|
||||
public string? Name { get; }
|
||||
@@ -71,13 +71,11 @@ namespace CliFx.Analyzers.ObjectModel
|
||||
{
|
||||
var attribute = TryGetOptionAttribute(property);
|
||||
|
||||
if (attribute is null)
|
||||
return null;
|
||||
|
||||
return FromAttribute(attribute);
|
||||
return attribute is not null
|
||||
? FromAttribute(attribute)
|
||||
: null;
|
||||
}
|
||||
|
||||
public static bool IsOptionProperty(IPropertySymbol property) =>
|
||||
TryGetOptionAttribute(property) is not null;
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,16 @@ using System.Linq;
|
||||
using CliFx.Analyzers.Utils.Extensions;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace CliFx.Analyzers.ObjectModel
|
||||
{
|
||||
namespace CliFx.Analyzers.ObjectModel;
|
||||
|
||||
internal partial class CommandParameterSymbol
|
||||
{
|
||||
public int Order { get; }
|
||||
|
||||
public string? Name { get; }
|
||||
|
||||
public bool? IsRequired { get; }
|
||||
|
||||
public ITypeSymbol? ConverterType { get; }
|
||||
|
||||
public IReadOnlyList<ITypeSymbol> ValidatorTypes { get; }
|
||||
@@ -18,11 +20,13 @@ namespace CliFx.Analyzers.ObjectModel
|
||||
public CommandParameterSymbol(
|
||||
int order,
|
||||
string? name,
|
||||
bool? isRequired,
|
||||
ITypeSymbol? converterType,
|
||||
IReadOnlyList<ITypeSymbol> validatorTypes)
|
||||
{
|
||||
Order = order;
|
||||
Name = name;
|
||||
IsRequired = isRequired;
|
||||
ConverterType = converterType;
|
||||
ValidatorTypes = validatorTypes;
|
||||
}
|
||||
@@ -48,6 +52,12 @@ namespace CliFx.Analyzers.ObjectModel
|
||||
.Select(a => a.Value.Value)
|
||||
.FirstOrDefault() as string;
|
||||
|
||||
var isRequired = attribute
|
||||
.NamedArguments
|
||||
.Where(a => a.Key == "IsRequired")
|
||||
.Select(a => a.Value.Value)
|
||||
.FirstOrDefault() as bool?;
|
||||
|
||||
var converter = attribute
|
||||
.NamedArguments
|
||||
.Where(a => a.Key == "Converter")
|
||||
@@ -63,20 +73,18 @@ namespace CliFx.Analyzers.ObjectModel
|
||||
.Cast<ITypeSymbol>()
|
||||
.ToArray();
|
||||
|
||||
return new CommandParameterSymbol(order, name, converter, validators);
|
||||
return new CommandParameterSymbol(order, name, isRequired, converter, validators);
|
||||
}
|
||||
|
||||
public static CommandParameterSymbol? TryResolve(IPropertySymbol property)
|
||||
{
|
||||
var attribute = TryGetParameterAttribute(property);
|
||||
|
||||
if (attribute is null)
|
||||
return null;
|
||||
|
||||
return FromAttribute(attribute);
|
||||
return attribute is not null
|
||||
? FromAttribute(attribute)
|
||||
: null;
|
||||
}
|
||||
|
||||
public static bool IsParameterProperty(IPropertySymbol property) =>
|
||||
TryGetParameterAttribute(property) is not null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace CliFx.Analyzers.ObjectModel
|
||||
{
|
||||
namespace CliFx.Analyzers.ObjectModel;
|
||||
|
||||
internal static class SymbolNames
|
||||
{
|
||||
public const string CliFxCommandInterface = "CliFx.ICommand";
|
||||
@@ -12,4 +12,3 @@
|
||||
public const string CliFxBindingValidatorInterface = "CliFx.Extensibility.IBindingValidator";
|
||||
public const string CliFxBindingValidatorClass = "CliFx.Extensibility.BindingValidator<T>";
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class OptionMustBeInsideCommandAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -48,4 +48,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class OptionMustHaveNameOrShortNameAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -37,4 +37,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class OptionMustHaveUniqueNameAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -62,4 +62,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class OptionMustHaveUniqueShortNameAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -61,4 +61,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class OptionMustHaveValidConverterAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -47,4 +47,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class OptionMustHaveValidNameAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -40,4 +40,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class OptionMustHaveValidShortNameAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -40,4 +40,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class OptionMustHaveValidValidatorsAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -49,4 +49,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class ParameterMustBeInsideCommandAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -48,4 +48,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
CliFx.Analyzers/ParameterMustBeLastIfNonRequiredAnalyzer.cs
Normal file
60
CliFx.Analyzers/ParameterMustBeLastIfNonRequiredAnalyzer.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System.Linq;
|
||||
using CliFx.Analyzers.ObjectModel;
|
||||
using CliFx.Analyzers.Utils.Extensions;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class ParameterMustBeLastIfNonRequiredAnalyzer : AnalyzerBase
|
||||
{
|
||||
public ParameterMustBeLastIfNonRequiredAnalyzer()
|
||||
: base(
|
||||
"Parameters marked as non-required must be the last in order",
|
||||
"This parameter is non-required so it must be the last in order (its order must be highest within the command).")
|
||||
{
|
||||
}
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
|
||||
var parameter = CommandParameterSymbol.TryResolve(property);
|
||||
if (parameter is null)
|
||||
return;
|
||||
|
||||
if (parameter.IsRequired != false)
|
||||
return;
|
||||
|
||||
var otherProperties = property
|
||||
.ContainingType
|
||||
.GetMembers()
|
||||
.OfType<IPropertySymbol>()
|
||||
.Where(m => !m.Equals(property, SymbolEqualityComparer.Default))
|
||||
.ToArray();
|
||||
|
||||
foreach (var otherProperty in otherProperties)
|
||||
{
|
||||
var otherParameter = CommandParameterSymbol.TryResolve(otherProperty);
|
||||
if (otherParameter is null)
|
||||
continue;
|
||||
|
||||
if (otherParameter.Order > parameter.Order)
|
||||
{
|
||||
context.ReportDiagnostic(CreateDiagnostic(propertyDeclaration.GetLocation()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
base.Initialize(context);
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,15 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class ParameterMustBeLastIfNonScalarAnalyzer : AnalyzerBase
|
||||
{
|
||||
public ParameterMustBeLastIfNonScalarAnalyzer()
|
||||
: base(
|
||||
"Parameters of non-scalar types must be last in order",
|
||||
"This parameter has a non-scalar type so it must be last in order (its order must be highest within the command).")
|
||||
"Parameters of non-scalar types must be the last in order",
|
||||
"This parameter has a non-scalar type so it must be the last in order (its order must be highest within the command).")
|
||||
{
|
||||
}
|
||||
|
||||
@@ -65,4 +65,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Linq;
|
||||
using CliFx.Analyzers.ObjectModel;
|
||||
using CliFx.Analyzers.Utils.Extensions;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class ParameterMustBeSingleIfNonRequiredAnalyzer : AnalyzerBase
|
||||
{
|
||||
public ParameterMustBeSingleIfNonRequiredAnalyzer()
|
||||
: base(
|
||||
"Parameters marked as non-required are limited to one per command",
|
||||
"This parameter is non-required so it must be the only such parameter in the command.")
|
||||
{
|
||||
}
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
|
||||
var parameter = CommandParameterSymbol.TryResolve(property);
|
||||
if (parameter is null)
|
||||
return;
|
||||
|
||||
if (parameter.IsRequired != false)
|
||||
return;
|
||||
|
||||
var otherProperties = property
|
||||
.ContainingType
|
||||
.GetMembers()
|
||||
.OfType<IPropertySymbol>()
|
||||
.Where(m => !m.Equals(property, SymbolEqualityComparer.Default))
|
||||
.ToArray();
|
||||
|
||||
foreach (var otherProperty in otherProperties)
|
||||
{
|
||||
var otherParameter = CommandParameterSymbol.TryResolve(otherProperty);
|
||||
if (otherParameter is null)
|
||||
continue;
|
||||
|
||||
if (otherParameter.IsRequired == false)
|
||||
{
|
||||
context.ReportDiagnostic(CreateDiagnostic(propertyDeclaration.GetLocation()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
base.Initialize(context);
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class ParameterMustBeSingleIfNonScalarAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -63,4 +63,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class ParameterMustHaveUniqueNameAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -62,4 +62,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class ParameterMustHaveUniqueOrderAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -55,4 +55,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class ParameterMustHaveValidConverterAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -47,4 +47,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class ParameterMustHaveValidValidatorsAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -49,4 +49,3 @@ namespace CliFx.Analyzers
|
||||
context.HandlePropertyDeclaration(Analyze);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers
|
||||
{
|
||||
namespace CliFx.Analyzers;
|
||||
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class SystemConsoleShouldBeAvoidedAnalyzer : AnalyzerBase
|
||||
{
|
||||
@@ -75,4 +75,3 @@ namespace CliFx.Analyzers
|
||||
context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.SimpleMemberAccessExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace CliFx.Analyzers.Utils.Extensions
|
||||
{
|
||||
namespace CliFx.Analyzers.Utils.Extensions;
|
||||
|
||||
internal static class RoslynExtensions
|
||||
{
|
||||
public static bool DisplayNameMatches(this ISymbol symbol, string name) =>
|
||||
@@ -50,4 +50,3 @@ namespace CliFx.Analyzers.Utils.Extensions
|
||||
}, SyntaxKind.PropertyDeclaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace CliFx.Analyzers.Utils.Extensions
|
||||
{
|
||||
namespace CliFx.Analyzers.Utils.Extensions;
|
||||
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string TrimEnd(
|
||||
@@ -15,4 +15,3 @@ namespace CliFx.Analyzers.Utils.Extensions
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using BenchmarkDotNet.Attributes;
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Benchmarks
|
||||
{
|
||||
namespace CliFx.Benchmarks;
|
||||
|
||||
public partial class Benchmarks
|
||||
{
|
||||
[Command]
|
||||
@@ -30,4 +30,3 @@ namespace CliFx.Benchmarks
|
||||
.Build()
|
||||
.RunAsync(Arguments, new Dictionary<string, string>());
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using clipr;
|
||||
|
||||
namespace CliFx.Benchmarks
|
||||
{
|
||||
namespace CliFx.Benchmarks;
|
||||
|
||||
public partial class Benchmarks
|
||||
{
|
||||
public class CliprCommand
|
||||
@@ -24,4 +24,3 @@ namespace CliFx.Benchmarks
|
||||
[Benchmark(Description = "Clipr")]
|
||||
public void ExecuteWithClipr() => CliParser.Parse<CliprCommand>(Arguments).Execute();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Cocona;
|
||||
|
||||
namespace CliFx.Benchmarks
|
||||
{
|
||||
namespace CliFx.Benchmarks;
|
||||
|
||||
public partial class Benchmarks
|
||||
{
|
||||
public class CoconaCommand
|
||||
@@ -21,4 +21,3 @@ namespace CliFx.Benchmarks
|
||||
[Benchmark(Description = "Cocona")]
|
||||
public void ExecuteWithCocona() => CoconaApp.Run<CoconaCommand>(Arguments);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using CommandLine;
|
||||
|
||||
namespace CliFx.Benchmarks
|
||||
{
|
||||
namespace CliFx.Benchmarks;
|
||||
|
||||
public partial class Benchmarks
|
||||
{
|
||||
public class CommandLineParserCommand
|
||||
@@ -27,4 +27,3 @@ namespace CliFx.Benchmarks
|
||||
.ParseArguments(Arguments, typeof(CommandLineParserCommand))
|
||||
.WithParsed<CommandLineParserCommand>(c => c.Execute());
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using McMaster.Extensions.CommandLineUtils;
|
||||
|
||||
namespace CliFx.Benchmarks
|
||||
{
|
||||
namespace CliFx.Benchmarks;
|
||||
|
||||
public partial class Benchmarks
|
||||
{
|
||||
public class McMasterCommand
|
||||
@@ -22,4 +22,3 @@ namespace CliFx.Benchmarks
|
||||
[Benchmark(Description = "McMaster.Extensions.CommandLineUtils")]
|
||||
public int ExecuteWithMcMaster() => CommandLineApplication.Execute<McMasterCommand>(Arguments);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using PowerArgs;
|
||||
|
||||
namespace CliFx.Benchmarks
|
||||
{
|
||||
namespace CliFx.Benchmarks;
|
||||
|
||||
public partial class Benchmarks
|
||||
{
|
||||
public class PowerArgsCommand
|
||||
@@ -24,4 +24,3 @@ namespace CliFx.Benchmarks
|
||||
[Benchmark(Description = "PowerArgs")]
|
||||
public void ExecuteWithPowerArgs() => Args.InvokeMain<PowerArgsCommand>(Arguments);
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ using System.CommandLine.Invocation;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace CliFx.Benchmarks
|
||||
{
|
||||
namespace CliFx.Benchmarks;
|
||||
|
||||
public partial class Benchmarks
|
||||
{
|
||||
public class SystemCommandLineCommand
|
||||
@@ -41,4 +41,3 @@ namespace CliFx.Benchmarks
|
||||
public async Task<int> ExecuteWithSystemCommandLine() =>
|
||||
await new SystemCommandLineCommand().ExecuteAsync(Arguments);
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Order;
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace CliFx.Benchmarks
|
||||
{
|
||||
namespace CliFx.Benchmarks;
|
||||
|
||||
[RankColumn]
|
||||
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
|
||||
public partial class Benchmarks
|
||||
@@ -17,4 +17,3 @@ namespace CliFx.Benchmarks
|
||||
.WithOptions(ConfigOptions.DisableOptimizationsValidator)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
||||
<PackageReference Include="clipr" Version="1.6.1" />
|
||||
<PackageReference Include="Cocona" Version="1.5.0" />
|
||||
<PackageReference Include="Cocona" Version="1.6.0" />
|
||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="3.1.0" />
|
||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.0.0" />
|
||||
<PackageReference Include="PowerArgs" Version="3.6.0" />
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20574.7" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ using CliFx.Demo.Utils;
|
||||
using CliFx.Exceptions;
|
||||
using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Demo.Commands
|
||||
{
|
||||
namespace CliFx.Demo.Commands;
|
||||
|
||||
[Command("book add", Description = "Add a book to the library.")]
|
||||
public partial class BookAddCommand : ICommand
|
||||
{
|
||||
@@ -67,4 +67,3 @@ namespace CliFx.Demo.Commands
|
||||
Random.Next(0, 9)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ using CliFx.Demo.Utils;
|
||||
using CliFx.Exceptions;
|
||||
using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Demo.Commands
|
||||
{
|
||||
namespace CliFx.Demo.Commands;
|
||||
|
||||
[Command("book", Description = "Retrieve a book from the library.")]
|
||||
public class BookCommand : ICommand
|
||||
{
|
||||
@@ -32,4 +32,3 @@ namespace CliFx.Demo.Commands
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using CliFx.Demo.Domain;
|
||||
using CliFx.Demo.Utils;
|
||||
using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Demo.Commands
|
||||
{
|
||||
namespace CliFx.Demo.Commands;
|
||||
|
||||
[Command("book list", Description = "List all books in the library.")]
|
||||
public class BookListCommand : ICommand
|
||||
{
|
||||
@@ -34,4 +34,3 @@ namespace CliFx.Demo.Commands
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ using CliFx.Demo.Domain;
|
||||
using CliFx.Exceptions;
|
||||
using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Demo.Commands
|
||||
{
|
||||
namespace CliFx.Demo.Commands;
|
||||
|
||||
[Command("book remove", Description = "Remove a book from the library.")]
|
||||
public class BookRemoveCommand : ICommand
|
||||
{
|
||||
@@ -33,4 +33,3 @@ namespace CliFx.Demo.Commands
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace CliFx.Demo.Domain
|
||||
{
|
||||
namespace CliFx.Demo.Domain;
|
||||
|
||||
public class Book
|
||||
{
|
||||
public string Title { get; }
|
||||
@@ -20,4 +20,3 @@ namespace CliFx.Demo.Domain
|
||||
Isbn = isbn;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace CliFx.Demo.Domain
|
||||
{
|
||||
namespace CliFx.Demo.Domain;
|
||||
|
||||
public partial class Isbn
|
||||
{
|
||||
public int EanPrefix { get; }
|
||||
@@ -42,4 +42,3 @@ namespace CliFx.Demo.Domain
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace CliFx.Demo.Domain
|
||||
{
|
||||
namespace CliFx.Demo.Domain;
|
||||
|
||||
public partial class Library
|
||||
{
|
||||
public IReadOnlyList<Book> Books { get; }
|
||||
@@ -33,4 +33,3 @@ namespace CliFx.Demo.Domain
|
||||
{
|
||||
public static Library Empty { get; } = new(Array.Empty<Book>());
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace CliFx.Demo.Domain
|
||||
{
|
||||
namespace CliFx.Demo.Domain;
|
||||
|
||||
public class LibraryProvider
|
||||
{
|
||||
private static string StorageFilePath { get; } = Path.Combine(Directory.GetCurrentDirectory(), "Library.json");
|
||||
@@ -38,4 +38,3 @@ namespace CliFx.Demo.Domain
|
||||
StoreLibrary(updatedLibrary);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,8 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using CliFx;
|
||||
using CliFx.Demo.Commands;
|
||||
using CliFx.Demo.Domain;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace CliFx.Demo
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
private static IServiceProvider GetServiceProvider()
|
||||
{
|
||||
// We use Microsoft.Extensions.DependencyInjection for injecting dependencies in commands
|
||||
var services = new ServiceCollection();
|
||||
|
||||
@@ -22,15 +15,11 @@ namespace CliFx.Demo
|
||||
services.AddTransient<BookRemoveCommand>();
|
||||
services.AddTransient<BookListCommand>();
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
public static async Task<int> Main() =>
|
||||
await new CliApplicationBuilder()
|
||||
return await new CliApplicationBuilder()
|
||||
.SetDescription("Demo application showcasing CliFx features.")
|
||||
.AddCommandsFromThisAssembly()
|
||||
.UseTypeActivator(GetServiceProvider().GetRequiredService)
|
||||
.UseTypeActivator(serviceProvider.GetRequiredService)
|
||||
.Build()
|
||||
.RunAsync();
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using CliFx.Demo.Domain;
|
||||
using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Demo.Utils
|
||||
{
|
||||
namespace CliFx.Demo.Utils;
|
||||
|
||||
internal static class ConsoleExtensions
|
||||
{
|
||||
public static void WriteBook(this ConsoleWriter writer, Book book)
|
||||
@@ -34,4 +34,3 @@ namespace CliFx.Demo.Utils
|
||||
writer.WriteLine(book.Isbn);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,8 +3,8 @@ using System.Threading.Tasks;
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Tests.Dummy.Commands
|
||||
{
|
||||
namespace CliFx.Tests.Dummy.Commands;
|
||||
|
||||
[Command("console-test")]
|
||||
public class ConsoleTestCommand : ICommand
|
||||
{
|
||||
@@ -21,4 +21,3 @@ namespace CliFx.Tests.Dummy.Commands
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Tests.Dummy.Commands
|
||||
{
|
||||
namespace CliFx.Tests.Dummy.Commands;
|
||||
|
||||
[Command("env-test")]
|
||||
public class EnvironmentTestCommand : ICommand
|
||||
{
|
||||
@@ -17,4 +17,3 @@ namespace CliFx.Tests.Dummy.Commands
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CliFx.Tests.Dummy
|
||||
{
|
||||
namespace CliFx.Tests.Dummy;
|
||||
// This dummy application is used in tests for scenarios
|
||||
// that require an external process to properly verify.
|
||||
|
||||
@@ -21,4 +20,3 @@ namespace CliFx.Tests.Dummy
|
||||
.Build()
|
||||
.RunAsync();
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public class ApplicationSpecs : SpecsBase
|
||||
{
|
||||
public ApplicationSpecs(ITestOutputHelper testOutput)
|
||||
@@ -83,4 +83,3 @@ namespace CliFx.Tests
|
||||
stdErr.Should().Contain("not a valid command");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public class CancellationSpecs : SpecsBase
|
||||
{
|
||||
public CancellationSpecs(ITestOutputHelper testOutput)
|
||||
@@ -64,4 +64,3 @@ public class Command : ICommand
|
||||
stdOut.Trim().Should().Be("Cancelled");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<CollectCoverage>true</CollectCoverage>
|
||||
<CoverletOutputFormat>opencover</CoverletOutputFormat>
|
||||
</PropertyGroup>
|
||||
@@ -13,15 +11,15 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Basic.Reference.Assemblies" Version="1.1.2" />
|
||||
<PackageReference Include="CliWrap" Version="3.3.2" />
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
<PackageReference Include="Basic.Reference.Assemblies" Version="1.2.4" />
|
||||
<PackageReference Include="CliWrap" Version="3.4.0" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.3.0" />
|
||||
<PackageReference Include="GitHubActionsTestLogger" Version="1.2.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="all" />
|
||||
<PackageReference Include="coverlet.msbuild" Version="3.0.3" PrivateAssets="all" />
|
||||
<PackageReference Include="coverlet.msbuild" Version="3.1.0" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -11,8 +11,8 @@ using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public class ConsoleSpecs : SpecsBase
|
||||
{
|
||||
public ConsoleSpecs(ITestOutputHelper testOutput)
|
||||
@@ -80,9 +80,9 @@ public class Command : ICommand
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
Console.OpenStandardInput().Should().NotBe(FakeConsole.Input.BaseStream);
|
||||
Console.OpenStandardOutput().Should().NotBe(FakeConsole.Output.BaseStream);
|
||||
Console.OpenStandardError().Should().NotBe(FakeConsole.Error.BaseStream);
|
||||
Console.OpenStandardInput().Should().NotBeSameAs(FakeConsole.Input.BaseStream);
|
||||
Console.OpenStandardOutput().Should().NotBeSameAs(FakeConsole.Output.BaseStream);
|
||||
Console.OpenStandardError().Should().NotBeSameAs(FakeConsole.Error.BaseStream);
|
||||
|
||||
Console.ForegroundColor.Should().NotBe(ConsoleColor.DarkMagenta);
|
||||
Console.BackgroundColor.Should().NotBe(ConsoleColor.DarkMagenta);
|
||||
@@ -156,4 +156,3 @@ public class Command : ICommand
|
||||
output.Should().Be("Hello world");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
public ConversionSpecs(ITestOutputHelper testOutput)
|
||||
@@ -990,4 +990,3 @@ public class Command : ICommand
|
||||
stdErr.Should().Contain("Hello world");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public class DirectivesSpecs : SpecsBase
|
||||
{
|
||||
public DirectivesSpecs(ITestOutputHelper testOutput)
|
||||
@@ -109,4 +109,3 @@ public class Command : ICommand
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public class EnvironmentSpecs : SpecsBase
|
||||
{
|
||||
public EnvironmentSpecs(ITestOutputHelper testOutput)
|
||||
@@ -257,4 +257,3 @@ public class Command : ICommand
|
||||
result.StandardOutput.Trim().Should().Be("Hello Mars!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public class ErrorReportingSpecs : SpecsBase
|
||||
{
|
||||
public ErrorReportingSpecs(ITestOutputHelper testOutput)
|
||||
@@ -202,4 +202,3 @@ public class Command : ICommand
|
||||
stdErr.Trim().Should().Be("Something went wrong");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
public HelpTextSpecs(ITestOutputHelper testOutput)
|
||||
@@ -371,6 +371,57 @@ public class Command : ICommand
|
||||
);
|
||||
}
|
||||
|
||||
// https://github.com/Tyrrrz/CliFx/issues/117
|
||||
[Fact]
|
||||
public async Task Help_text_shows_usage_format_which_lists_all_parameters_in_specified_order()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
@"
|
||||
// Base members appear last in reflection order
|
||||
public abstract class CommandBase : ICommand
|
||||
{
|
||||
[CommandParameter(0)]
|
||||
public string Foo { get; set; }
|
||||
|
||||
public abstract ValueTask ExecuteAsync(IConsole console);
|
||||
}
|
||||
|
||||
[Command]
|
||||
public class Command : CommandBase
|
||||
{
|
||||
[CommandParameter(2)]
|
||||
public IReadOnlyList<string> Baz { get; set; }
|
||||
|
||||
[CommandParameter(1)]
|
||||
public string Bar { get; set; }
|
||||
|
||||
public override ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
");
|
||||
|
||||
var application = new CliApplicationBuilder()
|
||||
.AddCommand(commandType)
|
||||
.UseConsole(FakeConsole)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
"USAGE",
|
||||
"<foo>", "<bar>", "<baz...>"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Help_text_shows_usage_format_which_lists_all_required_options()
|
||||
{
|
||||
@@ -965,4 +1016,3 @@ public class SecondCommandSecondChildCommand : ICommand
|
||||
stdOut.Trim().Should().Be("v6.9");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
public OptionBindingSpecs(ITestOutputHelper testOutput)
|
||||
@@ -496,6 +496,83 @@ public class Command : ICommand
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Option_binding_supports_multiple_inheritance_through_default_interface_members()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
@"
|
||||
public static class SharedContext
|
||||
{
|
||||
public static int Foo { get; set; }
|
||||
|
||||
public static bool Bar { get; set; }
|
||||
}
|
||||
|
||||
public interface IHasFoo : ICommand
|
||||
{
|
||||
[CommandOption(""foo"")]
|
||||
public int Foo
|
||||
{
|
||||
get => SharedContext.Foo;
|
||||
set => SharedContext.Foo = value;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IHasBar : ICommand
|
||||
{
|
||||
[CommandOption(""bar"")]
|
||||
public bool Bar
|
||||
{
|
||||
get => SharedContext.Bar;
|
||||
set => SharedContext.Bar = value;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IHasBaz : ICommand
|
||||
{
|
||||
public string Baz { get; set; }
|
||||
}
|
||||
|
||||
[Command]
|
||||
public class Command : IHasFoo, IHasBar, IHasBaz
|
||||
{
|
||||
[CommandOption(""baz"")]
|
||||
public string Baz { get; set; }
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
console.Output.WriteLine(""Foo = "" + SharedContext.Foo);
|
||||
console.Output.WriteLine(""Bar = "" + SharedContext.Bar);
|
||||
console.Output.WriteLine(""Baz = "" + Baz);
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
var application = new CliApplicationBuilder()
|
||||
.AddCommand(commandType)
|
||||
.UseConsole(FakeConsole)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] { "--foo", "42", "--bar", "--baz", "xyz" }
|
||||
);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = 42",
|
||||
"Bar = True",
|
||||
"Baz = xyz"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Option_binding_does_not_consider_a_negative_number_as_an_option_name_or_short_name()
|
||||
{
|
||||
@@ -705,4 +782,3 @@ public class Command : ICommand
|
||||
stdErr.Should().Contain("expects a single argument, but provided with multiple");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public class ParameterBindingSpecs : SpecsBase
|
||||
{
|
||||
public ParameterBindingSpecs(ITestOutputHelper testOutput)
|
||||
@@ -120,7 +120,53 @@ public class Command : ICommand
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parameter_binding_fails_if_one_of_the_parameters_has_not_been_provided()
|
||||
public async Task Parameter_is_not_bound_if_there_are_no_arguments_matching_its_order()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
@"
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
{
|
||||
[CommandParameter(0)]
|
||||
public string Foo { get; set; }
|
||||
|
||||
[CommandParameter(1, IsRequired = false)]
|
||||
public string Bar { get; set; } = ""xyz"";
|
||||
|
||||
public ValueTask ExecuteAsync(IConsole console)
|
||||
{
|
||||
console.Output.WriteLine(""Foo = "" + Foo);
|
||||
console.Output.WriteLine(""Bar = "" + Bar);
|
||||
|
||||
return default;
|
||||
}
|
||||
}");
|
||||
|
||||
var application = new CliApplicationBuilder()
|
||||
.AddCommand(commandType)
|
||||
.UseConsole(FakeConsole)
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"abc"},
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = abc",
|
||||
"Bar = xyz"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Parameter_binding_fails_if_a_required_parameter_has_not_been_provided()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
@@ -153,7 +199,7 @@ public class Command : ICommand
|
||||
|
||||
// Assert
|
||||
exitCode.Should().NotBe(0);
|
||||
stdErr.Should().Contain("Missing parameter(s)");
|
||||
stdErr.Should().Contain("Missing required parameter(s)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -190,7 +236,7 @@ public class Command : ICommand
|
||||
|
||||
// Assert
|
||||
exitCode.Should().NotBe(0);
|
||||
stdErr.Should().Contain("Missing parameter(s)");
|
||||
stdErr.Should().Contain("Missing required parameter(s)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -230,4 +276,3 @@ public class Command : ICommand
|
||||
stdErr.Should().Contain("Unexpected parameter(s)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public class RoutingSpecs : SpecsBase
|
||||
{
|
||||
public RoutingSpecs(ITestOutputHelper testOutput)
|
||||
@@ -183,4 +183,3 @@ public class NamedChildCommand : ICommand
|
||||
stdOut.Trim().Should().Be("cmd child");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ using CliFx.Infrastructure;
|
||||
using CliFx.Tests.Utils.Extensions;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public abstract class SpecsBase : IDisposable
|
||||
{
|
||||
public ITestOutputHelper TestOutput { get; }
|
||||
@@ -20,4 +20,3 @@ namespace CliFx.Tests
|
||||
FakeConsole.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ using FluentAssertions;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests
|
||||
{
|
||||
namespace CliFx.Tests;
|
||||
|
||||
public class TypeActivationSpecs : SpecsBase
|
||||
{
|
||||
public TypeActivationSpecs(ITestOutputHelper testOutput)
|
||||
@@ -162,4 +162,3 @@ public class Command : ICommand
|
||||
stdErr.Should().Contain("Failed to create an instance of type");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace CliFx.Tests.Utils
|
||||
{
|
||||
namespace CliFx.Tests.Utils;
|
||||
|
||||
// This class uses Roslyn to compile commands dynamically.
|
||||
//
|
||||
// It allows us to collocate commands with tests more
|
||||
@@ -106,13 +106,13 @@ namespace CliFx.Tests.Utils
|
||||
// Return all defined commands
|
||||
var commandTypes = generatedAssembly
|
||||
.GetTypes()
|
||||
.Where(t => t.IsAssignableTo(typeof(ICommand)))
|
||||
.Where(t => t.IsAssignableTo(typeof(ICommand)) && !t.IsAbstract)
|
||||
.ToArray();
|
||||
|
||||
if (commandTypes.Length <= 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"There are no command definitions in the provide source code."
|
||||
"There are no command definitions in the provided source code."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -126,11 +126,10 @@ namespace CliFx.Tests.Utils
|
||||
if (commandTypes.Count > 1)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"There are more than one command definitions in the provide source code."
|
||||
"There are more than one command definitions in the provided source code."
|
||||
);
|
||||
}
|
||||
|
||||
return commandTypes.Single();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Collections;
|
||||
using FluentAssertions.Execution;
|
||||
using FluentAssertions.Primitives;
|
||||
|
||||
namespace CliFx.Tests.Utils.Extensions
|
||||
{
|
||||
namespace CliFx.Tests.Utils.Extensions;
|
||||
|
||||
internal static class AssertionExtensions
|
||||
{
|
||||
public static AndConstraint<StringCollectionAssertions> ConsistOfLines(
|
||||
public static void ConsistOfLines(
|
||||
this StringAssertions assertions,
|
||||
IEnumerable<string> lines)
|
||||
{
|
||||
var actualLines = assertions.Subject.Split(new[] {'\n', '\r'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
return actualLines.Should().Equal(lines);
|
||||
actualLines.Should().Equal(lines);
|
||||
}
|
||||
|
||||
public static AndConstraint<StringCollectionAssertions> ConsistOfLines(
|
||||
public static void ConsistOfLines(
|
||||
this StringAssertions assertions,
|
||||
params string[] lines) =>
|
||||
assertions.ConsistOfLines((IEnumerable<string>) lines);
|
||||
@@ -51,4 +49,3 @@ namespace CliFx.Tests.Utils.Extensions
|
||||
params string[] values) =>
|
||||
assertions.ContainAllInOrder((IEnumerable<string>) values);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using CliFx.Infrastructure;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace CliFx.Tests.Utils.Extensions
|
||||
{
|
||||
namespace CliFx.Tests.Utils.Extensions;
|
||||
|
||||
internal static class ConsoleExtensions
|
||||
{
|
||||
public static void DumpToTestOutput(this FakeInMemoryConsole console, ITestOutputHelper testOutputHelper)
|
||||
@@ -14,4 +14,3 @@ namespace CliFx.Tests.Utils.Extensions
|
||||
testOutputHelper.WriteLine(console.ReadErrorString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,10 @@
|
||||
using CliFx.Attributes;
|
||||
using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Tests.Utils
|
||||
{
|
||||
namespace CliFx.Tests.Utils;
|
||||
|
||||
[Command]
|
||||
public class NoOpCommand : ICommand
|
||||
{
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace CliFx
|
||||
{
|
||||
namespace CliFx;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration of an application.
|
||||
/// </summary>
|
||||
@@ -36,4 +36,3 @@ namespace CliFx
|
||||
IsPreviewModeAllowed = isPreviewModeAllowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user