Refactor with C# 12 features

This commit is contained in:
Tyrrrz
2023-12-10 22:51:57 +02:00
parent 5854f36756
commit 490398f773
68 changed files with 371 additions and 622 deletions

View File

@@ -11,7 +11,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Basic.Reference.Assemblies.Net70" Version="1.4.5" /> <PackageReference Include="Basic.Reference.Assemblies.Net70" Version="1.4.5" />
<PackageReference Include="coverlet.collector" Version="6.0.0" PrivateAssets="all" /> <PackageReference Include="coverlet.collector" Version="6.0.0" PrivateAssets="all" />
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" /> <PackageReference Include="CSharpier.MsBuild" Version="0.26.5" PrivateAssets="all" />
<PackageReference Include="GitHubActionsTestLogger" Version="2.3.3" PrivateAssets="all" /> <PackageReference Include="GitHubActionsTestLogger" Version="2.3.3" PrivateAssets="all" />
<PackageReference Include="FluentAssertions" Version="6.12.0" /> <PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />

View File

@@ -13,12 +13,10 @@ using Microsoft.CodeAnalysis.Text;
namespace CliFx.Analyzers.Tests.Utils; namespace CliFx.Analyzers.Tests.Utils;
internal class AnalyzerAssertions : ReferenceTypeAssertions<DiagnosticAnalyzer, AnalyzerAssertions> internal class AnalyzerAssertions(DiagnosticAnalyzer analyzer)
: ReferenceTypeAssertions<DiagnosticAnalyzer, AnalyzerAssertions>(analyzer)
{ {
protected override string Identifier { get; } = "analyzer"; protected override string Identifier => "analyzer";
public AnalyzerAssertions(DiagnosticAnalyzer analyzer)
: base(analyzer) { }
private Compilation Compile(string sourceCode) private Compilation Compile(string sourceCode)
{ {

View File

@@ -17,7 +17,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" /> <PackageReference Include="CSharpier.MsBuild" Version="0.26.5" PrivateAssets="all" />
<!-- Make sure to target the lowest possible version of the compiler for wider support --> <!-- Make sure to target the lowest possible version of the compiler for wider support -->
<PackageReference Include="Microsoft.CodeAnalysis" Version="3.0.0" PrivateAssets="all" /> <PackageReference Include="Microsoft.CodeAnalysis" Version="3.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.0.0" PrivateAssets="all" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.0.0" PrivateAssets="all" />

View File

@@ -8,14 +8,12 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class CommandMustBeAnnotatedAnalyzer : AnalyzerBase public class CommandMustBeAnnotatedAnalyzer()
{ : AnalyzerBase(
public CommandMustBeAnnotatedAnalyzer()
: base(
$"Commands must be annotated with `{SymbolNames.CliFxCommandAttribute}`", $"Commands must be annotated with `{SymbolNames.CliFxCommandAttribute}`",
$"This type must be annotated with `{SymbolNames.CliFxCommandAttribute}` in order to be a valid command." $"This type must be annotated with `{SymbolNames.CliFxCommandAttribute}` in order to be a valid command."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
ClassDeclarationSyntax classDeclaration, ClassDeclarationSyntax classDeclaration,

View File

@@ -8,14 +8,12 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class CommandMustImplementInterfaceAnalyzer : AnalyzerBase public class CommandMustImplementInterfaceAnalyzer()
{ : AnalyzerBase(
public CommandMustImplementInterfaceAnalyzer()
: base(
$"Commands must implement `{SymbolNames.CliFxCommandInterface}` interface", $"Commands must implement `{SymbolNames.CliFxCommandInterface}` interface",
$"This type must implement `{SymbolNames.CliFxCommandInterface}` interface in order to be a valid command." $"This type must implement `{SymbolNames.CliFxCommandInterface}` interface in order to be a valid command."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
ClassDeclarationSyntax classDeclaration, ClassDeclarationSyntax classDeclaration,

View File

@@ -5,36 +5,26 @@ using Microsoft.CodeAnalysis;
namespace CliFx.Analyzers.ObjectModel; namespace CliFx.Analyzers.ObjectModel;
internal partial class CommandOptionSymbol : ICommandMemberSymbol internal partial class CommandOptionSymbol(
{
public IPropertySymbol Property { get; }
public string? Name { get; }
public char? ShortName { get; }
public bool? IsRequired { get; }
public ITypeSymbol? ConverterType { get; }
public IReadOnlyList<ITypeSymbol> ValidatorTypes { get; }
public CommandOptionSymbol(
IPropertySymbol property, IPropertySymbol property,
string? name, string? name,
char? shortName, char? shortName,
bool? isRequired, bool? isRequired,
ITypeSymbol? converterType, ITypeSymbol? converterType,
IReadOnlyList<ITypeSymbol> validatorTypes IReadOnlyList<ITypeSymbol> validatorTypes
) ) : ICommandMemberSymbol
{ {
Property = property; public IPropertySymbol Property { get; } = property;
Name = name;
ShortName = shortName; public string? Name { get; } = name;
IsRequired = isRequired;
ConverterType = converterType; public char? ShortName { get; } = shortName;
ValidatorTypes = validatorTypes;
} public bool? IsRequired { get; } = isRequired;
public ITypeSymbol? ConverterType { get; } = converterType;
public IReadOnlyList<ITypeSymbol> ValidatorTypes { get; } = validatorTypes;
} }
internal partial class CommandOptionSymbol internal partial class CommandOptionSymbol

View File

@@ -5,36 +5,26 @@ using Microsoft.CodeAnalysis;
namespace CliFx.Analyzers.ObjectModel; namespace CliFx.Analyzers.ObjectModel;
internal partial class CommandParameterSymbol : ICommandMemberSymbol internal partial class CommandParameterSymbol(
{
public IPropertySymbol Property { get; }
public int Order { get; }
public string? Name { get; }
public bool? IsRequired { get; }
public ITypeSymbol? ConverterType { get; }
public IReadOnlyList<ITypeSymbol> ValidatorTypes { get; }
public CommandParameterSymbol(
IPropertySymbol property, IPropertySymbol property,
int order, int order,
string? name, string? name,
bool? isRequired, bool? isRequired,
ITypeSymbol? converterType, ITypeSymbol? converterType,
IReadOnlyList<ITypeSymbol> validatorTypes IReadOnlyList<ITypeSymbol> validatorTypes
) ) : ICommandMemberSymbol
{ {
Property = property; public IPropertySymbol Property { get; } = property;
Order = order;
Name = name; public int Order { get; } = order;
IsRequired = isRequired;
ConverterType = converterType; public string? Name { get; } = name;
ValidatorTypes = validatorTypes;
} public bool? IsRequired { get; } = isRequired;
public ITypeSymbol? ConverterType { get; } = converterType;
public IReadOnlyList<ITypeSymbol> ValidatorTypes { get; } = validatorTypes;
} }
internal partial class CommandParameterSymbol internal partial class CommandParameterSymbol

View File

@@ -8,14 +8,12 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustBeInsideCommandAnalyzer : AnalyzerBase public class OptionMustBeInsideCommandAnalyzer()
{ : AnalyzerBase(
public OptionMustBeInsideCommandAnalyzer()
: base(
"Options must be defined inside commands", "Options must be defined inside commands",
$"This option must be defined inside a class that implements `{SymbolNames.CliFxCommandInterface}`." $"This option must be defined inside a class that implements `{SymbolNames.CliFxCommandInterface}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -7,14 +7,12 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustBeRequiredIfPropertyRequiredAnalyzer : AnalyzerBase public class OptionMustBeRequiredIfPropertyRequiredAnalyzer()
{ : AnalyzerBase(
public OptionMustBeRequiredIfPropertyRequiredAnalyzer()
: base(
"Options bound to required properties cannot be marked as non-required", "Options bound to required properties cannot be marked as non-required",
"This option cannot be marked as non-required because it's bound to a required property." "This option cannot be marked as non-required because it's bound to a required property."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -7,14 +7,12 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveNameOrShortNameAnalyzer : AnalyzerBase public class OptionMustHaveNameOrShortNameAnalyzer()
{ : AnalyzerBase(
public OptionMustHaveNameOrShortNameAnalyzer()
: base(
"Options must have either a name or short name specified", "Options must have either a name or short name specified",
"This option must have either a name or short name specified." "This option must have either a name or short name specified."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -9,16 +9,14 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveUniqueNameAnalyzer : AnalyzerBase public class OptionMustHaveUniqueNameAnalyzer()
{ : AnalyzerBase(
public OptionMustHaveUniqueNameAnalyzer()
: base(
"Options must have unique names", "Options must have unique names",
"This option's name must be unique within the command (comparison IS NOT case sensitive). " "This option's name must be unique within the command (comparison IS NOT case sensitive). "
+ "Specified name: `{0}`. " + "Specified name: `{0}`. "
+ "Property bound to another option with the same name: `{1}`." + "Property bound to another option with the same name: `{1}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -8,16 +8,14 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveUniqueShortNameAnalyzer : AnalyzerBase public class OptionMustHaveUniqueShortNameAnalyzer()
{ : AnalyzerBase(
public OptionMustHaveUniqueShortNameAnalyzer()
: base(
"Options must have unique short names", "Options must have unique short names",
"This option's short name must be unique within the command (comparison IS case sensitive). " "This option's short name must be unique within the command (comparison IS case sensitive). "
+ "Specified short name: `{0}` " + "Specified short name: `{0}` "
+ "Property bound to another option with the same short name: `{1}`." + "Property bound to another option with the same short name: `{1}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -8,14 +8,12 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveValidConverterAnalyzer : AnalyzerBase public class OptionMustHaveValidConverterAnalyzer()
{ : AnalyzerBase(
public OptionMustHaveValidConverterAnalyzer()
: base(
$"Option converters must derive from `{SymbolNames.CliFxBindingConverterClass}`", $"Option converters must derive from `{SymbolNames.CliFxBindingConverterClass}`",
$"Converter specified for this option must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`." $"Converter specified for this option must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -7,15 +7,13 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveValidNameAnalyzer : AnalyzerBase public class OptionMustHaveValidNameAnalyzer()
{ : AnalyzerBase(
public OptionMustHaveValidNameAnalyzer()
: base(
"Options must have valid names", "Options must have valid names",
"This option's name must be at least 2 characters long and must start with a letter. " "This option's name must be at least 2 characters long and must start with a letter. "
+ "Specified name: `{0}`." + "Specified name: `{0}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -7,15 +7,13 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveValidShortNameAnalyzer : AnalyzerBase public class OptionMustHaveValidShortNameAnalyzer()
{ : AnalyzerBase(
public OptionMustHaveValidShortNameAnalyzer()
: base(
"Option short names must be letter characters", "Option short names must be letter characters",
"This option's short name must be a single letter character. " "This option's short name must be a single letter character. "
+ "Specified short name: `{0}`." + "Specified short name: `{0}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -8,14 +8,12 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveValidValidatorsAnalyzer : AnalyzerBase public class OptionMustHaveValidValidatorsAnalyzer()
{ : AnalyzerBase(
public OptionMustHaveValidValidatorsAnalyzer()
: base(
$"Option validators must derive from `{SymbolNames.CliFxBindingValidatorClass}`", $"Option validators must derive from `{SymbolNames.CliFxBindingValidatorClass}`",
$"Each validator specified for this option must derive from a compatible `{SymbolNames.CliFxBindingValidatorClass}`." $"Each validator specified for this option must derive from a compatible `{SymbolNames.CliFxBindingValidatorClass}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -8,14 +8,12 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustBeInsideCommandAnalyzer : AnalyzerBase public class ParameterMustBeInsideCommandAnalyzer()
{ : AnalyzerBase(
public ParameterMustBeInsideCommandAnalyzer()
: base(
"Parameters must be defined inside commands", "Parameters must be defined inside commands",
$"This parameter must be defined inside a class that implements `{SymbolNames.CliFxCommandInterface}`." $"This parameter must be defined inside a class that implements `{SymbolNames.CliFxCommandInterface}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -8,15 +8,13 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustBeLastIfNonRequiredAnalyzer : AnalyzerBase public class ParameterMustBeLastIfNonRequiredAnalyzer()
{ : AnalyzerBase(
public ParameterMustBeLastIfNonRequiredAnalyzer()
: base(
"Parameters marked as non-required must be the last in order", "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). " "This parameter is non-required so it must be the last in order (its order must be highest within the command). "
+ "Property bound to another non-required parameter: `{0}`." + "Property bound to another non-required parameter: `{0}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -8,15 +8,13 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustBeLastIfNonScalarAnalyzer : AnalyzerBase public class ParameterMustBeLastIfNonScalarAnalyzer()
{ : AnalyzerBase(
public ParameterMustBeLastIfNonScalarAnalyzer()
: base(
"Parameters of non-scalar types must be the last in order", "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). " "This parameter has a non-scalar type so it must be the last in order (its order must be highest within the command). "
+ "Property bound to another non-scalar parameter: `{0}`." + "Property bound to another non-scalar parameter: `{0}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -7,14 +7,12 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustBeRequiredIfPropertyRequiredAnalyzer : AnalyzerBase public class ParameterMustBeRequiredIfPropertyRequiredAnalyzer()
{ : AnalyzerBase(
public ParameterMustBeRequiredIfPropertyRequiredAnalyzer()
: base(
"Parameters bound to required properties cannot be marked as non-required", "Parameters bound to required properties cannot be marked as non-required",
"This parameter cannot be marked as non-required because it's bound to a required property." "This parameter cannot be marked as non-required because it's bound to a required property."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -8,15 +8,13 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustBeSingleIfNonRequiredAnalyzer : AnalyzerBase public class ParameterMustBeSingleIfNonRequiredAnalyzer()
{ : AnalyzerBase(
public ParameterMustBeSingleIfNonRequiredAnalyzer()
: base(
"Parameters marked as non-required are limited to one per command", "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. " "This parameter is non-required so it must be the only such parameter in the command. "
+ "Property bound to another non-required parameter: `{0}`." + "Property bound to another non-required parameter: `{0}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -8,15 +8,13 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustBeSingleIfNonScalarAnalyzer : AnalyzerBase public class ParameterMustBeSingleIfNonScalarAnalyzer()
{ : AnalyzerBase(
public ParameterMustBeSingleIfNonScalarAnalyzer()
: base(
"Parameters of non-scalar types are limited to one per command", "Parameters of non-scalar types are limited to one per command",
"This parameter has a non-scalar type so it must be the only such parameter in the command. " "This parameter has a non-scalar type so it must be the only such parameter in the command. "
+ "Property bound to another non-scalar parameter: `{0}`." + "Property bound to another non-scalar parameter: `{0}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -9,16 +9,14 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustHaveUniqueNameAnalyzer : AnalyzerBase public class ParameterMustHaveUniqueNameAnalyzer()
{ : AnalyzerBase(
public ParameterMustHaveUniqueNameAnalyzer()
: base(
"Parameters must have unique names", "Parameters must have unique names",
"This parameter's name must be unique within the command (comparison IS NOT case sensitive). " "This parameter's name must be unique within the command (comparison IS NOT case sensitive). "
+ "Specified name: `{0}`. " + "Specified name: `{0}`. "
+ "Property bound to another parameter with the same name: `{1}`." + "Property bound to another parameter with the same name: `{1}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -8,16 +8,14 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustHaveUniqueOrderAnalyzer : AnalyzerBase public class ParameterMustHaveUniqueOrderAnalyzer()
{ : AnalyzerBase(
public ParameterMustHaveUniqueOrderAnalyzer()
: base(
"Parameters must have unique order", "Parameters must have unique order",
"This parameter's order must be unique within the command. " "This parameter's order must be unique within the command. "
+ "Specified order: {0}. " + "Specified order: {0}. "
+ "Property bound to another parameter with the same order: `{1}`." + "Property bound to another parameter with the same order: `{1}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -8,14 +8,12 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustHaveValidConverterAnalyzer : AnalyzerBase public class ParameterMustHaveValidConverterAnalyzer()
{ : AnalyzerBase(
public ParameterMustHaveValidConverterAnalyzer()
: base(
$"Parameter converters must derive from `{SymbolNames.CliFxBindingConverterClass}`", $"Parameter converters must derive from `{SymbolNames.CliFxBindingConverterClass}`",
$"Converter specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`." $"Converter specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -8,14 +8,12 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustHaveValidValidatorsAnalyzer : AnalyzerBase public class ParameterMustHaveValidValidatorsAnalyzer()
{ : AnalyzerBase(
public ParameterMustHaveValidValidatorsAnalyzer()
: base(
$"Parameter validators must derive from `{SymbolNames.CliFxBindingValidatorClass}`", $"Parameter validators must derive from `{SymbolNames.CliFxBindingValidatorClass}`",
$"Each validator specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingValidatorClass}`." $"Each validator specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingValidatorClass}`."
) { } )
{
private void Analyze( private void Analyze(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
PropertyDeclarationSyntax propertyDeclaration, PropertyDeclarationSyntax propertyDeclaration,

View File

@@ -9,15 +9,13 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers; namespace CliFx.Analyzers;
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class SystemConsoleShouldBeAvoidedAnalyzer : AnalyzerBase public class SystemConsoleShouldBeAvoidedAnalyzer()
{ : AnalyzerBase(
public SystemConsoleShouldBeAvoidedAnalyzer()
: base(
$"Avoid calling `System.Console` where `{SymbolNames.CliFxConsoleInterface}` is available", $"Avoid calling `System.Console` where `{SymbolNames.CliFxConsoleInterface}` is available",
$"Use the provided `{SymbolNames.CliFxConsoleInterface}` abstraction instead of `System.Console` to ensure that the command can be tested in isolation.", $"Use the provided `{SymbolNames.CliFxConsoleInterface}` abstraction instead of `System.Console` to ensure that the command can be tested in isolation.",
DiagnosticSeverity.Warning DiagnosticSeverity.Warning
) { } )
{
private MemberAccessExpressionSyntax? TryGetSystemConsoleMemberAccess( private MemberAccessExpressionSyntax? TryGetSystemConsoleMemberAccess(
SyntaxNodeAnalysisContext context, SyntaxNodeAnalysisContext context,
SyntaxNode node SyntaxNode node

View File

@@ -6,11 +6,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.10" /> <PackageReference Include="BenchmarkDotNet" Version="0.13.11" />
<PackageReference Include="clipr" Version="1.6.1" /> <PackageReference Include="clipr" Version="1.6.1" />
<PackageReference Include="Cocona" Version="2.2.0" /> <PackageReference Include="Cocona" Version="2.2.0" />
<PackageReference Include="CommandLineParser" Version="2.9.1" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" /> <PackageReference Include="CSharpier.MsBuild" Version="0.26.5" PrivateAssets="all" />
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.1.0" /> <PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.1.0" />
<PackageReference Include="PowerArgs" Version="4.0.3" /> <PackageReference Include="PowerArgs" Version="4.0.3" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20574.7" /> <PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20574.7" />

View File

@@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" /> <PackageReference Include="CSharpier.MsBuild" Version="0.26.5" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
</ItemGroup> </ItemGroup>

View File

@@ -9,10 +9,8 @@ using CliFx.Infrastructure;
namespace CliFx.Demo.Commands; namespace CliFx.Demo.Commands;
[Command("book add", Description = "Adds a book to the library.")] [Command("book add", Description = "Adds a book to the library.")]
public partial class BookAddCommand : ICommand public partial class BookAddCommand(LibraryProvider libraryProvider) : ICommand
{ {
private readonly LibraryProvider _libraryProvider;
[CommandParameter(0, Name = "title", Description = "Book title.")] [CommandParameter(0, Name = "title", Description = "Book title.")]
public required string Title { get; init; } public required string Title { get; init; }
@@ -25,18 +23,13 @@ public partial class BookAddCommand : ICommand
[CommandOption("isbn", 'n', Description = "Book ISBN.")] [CommandOption("isbn", 'n', Description = "Book ISBN.")]
public Isbn Isbn { get; init; } = CreateRandomIsbn(); public Isbn Isbn { get; init; } = CreateRandomIsbn();
public BookAddCommand(LibraryProvider libraryProvider)
{
_libraryProvider = libraryProvider;
}
public ValueTask ExecuteAsync(IConsole console) public ValueTask ExecuteAsync(IConsole console)
{ {
if (_libraryProvider.TryGetBook(Title) is not null) if (libraryProvider.TryGetBook(Title) is not null)
throw new CommandException("Book already exists.", 10); throw new CommandException("Book already exists.", 10);
var book = new Book(Title, Author, Published, Isbn); var book = new Book(Title, Author, Published, Isbn);
_libraryProvider.AddBook(book); libraryProvider.AddBook(book);
console.Output.WriteLine("Book added."); console.Output.WriteLine("Book added.");
console.Output.WriteBook(book); console.Output.WriteBook(book);

View File

@@ -8,21 +8,14 @@ using CliFx.Infrastructure;
namespace CliFx.Demo.Commands; namespace CliFx.Demo.Commands;
[Command("book", Description = "Retrieves a book from the library.")] [Command("book", Description = "Retrieves a book from the library.")]
public class BookCommand : ICommand public class BookCommand(LibraryProvider libraryProvider) : ICommand
{ {
private readonly LibraryProvider _libraryProvider;
[CommandParameter(0, Name = "title", Description = "Title of the book to retrieve.")] [CommandParameter(0, Name = "title", Description = "Title of the book to retrieve.")]
public required string Title { get; init; } public required string Title { get; init; }
public BookCommand(LibraryProvider libraryProvider)
{
_libraryProvider = libraryProvider;
}
public ValueTask ExecuteAsync(IConsole console) public ValueTask ExecuteAsync(IConsole console)
{ {
var book = _libraryProvider.TryGetBook(Title); var book = libraryProvider.TryGetBook(Title);
if (book is null) if (book is null)
throw new CommandException("Book not found.", 10); throw new CommandException("Book not found.", 10);

View File

@@ -7,18 +7,11 @@ using CliFx.Infrastructure;
namespace CliFx.Demo.Commands; namespace CliFx.Demo.Commands;
[Command("book list", Description = "Lists all books in the library.")] [Command("book list", Description = "Lists all books in the library.")]
public class BookListCommand : ICommand public class BookListCommand(LibraryProvider libraryProvider) : ICommand
{ {
private readonly LibraryProvider _libraryProvider;
public BookListCommand(LibraryProvider libraryProvider)
{
_libraryProvider = libraryProvider;
}
public ValueTask ExecuteAsync(IConsole console) public ValueTask ExecuteAsync(IConsole console)
{ {
var library = _libraryProvider.GetLibrary(); var library = libraryProvider.GetLibrary();
for (var i = 0; i < library.Books.Count; i++) for (var i = 0; i < library.Books.Count; i++)
{ {

View File

@@ -7,26 +7,19 @@ using CliFx.Infrastructure;
namespace CliFx.Demo.Commands; namespace CliFx.Demo.Commands;
[Command("book remove", Description = "Removes a book from the library.")] [Command("book remove", Description = "Removes a book from the library.")]
public class BookRemoveCommand : ICommand public class BookRemoveCommand(LibraryProvider libraryProvider) : ICommand
{ {
private readonly LibraryProvider _libraryProvider;
[CommandParameter(0, Name = "title", Description = "Title of the book to remove.")] [CommandParameter(0, Name = "title", Description = "Title of the book to remove.")]
public required string Title { get; init; } public required string Title { get; init; }
public BookRemoveCommand(LibraryProvider libraryProvider)
{
_libraryProvider = libraryProvider;
}
public ValueTask ExecuteAsync(IConsole console) public ValueTask ExecuteAsync(IConsole console)
{ {
var book = _libraryProvider.TryGetBook(Title); var book = libraryProvider.TryGetBook(Title);
if (book is null) if (book is null)
throw new CommandException("Book not found.", 10); throw new CommandException("Book not found.", 10);
_libraryProvider.RemoveBook(book); libraryProvider.RemoveBook(book);
console.Output.WriteLine($"Book {Title} removed."); console.Output.WriteLine($"Book {Title} removed.");

View File

@@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" /> <PackageReference Include="CSharpier.MsBuild" Version="0.26.5" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -8,11 +8,8 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public class ApplicationSpecs : SpecsBase public class ApplicationSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
{ {
public ApplicationSpecs(ITestOutputHelper testOutput)
: base(testOutput) { }
[Fact] [Fact]
public async Task I_can_create_an_application_with_the_default_configuration() public async Task I_can_create_an_application_with_the_default_configuration()
{ {

View File

@@ -12,11 +12,8 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public class CancellationSpecs : SpecsBase public class CancellationSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
{ {
public CancellationSpecs(ITestOutputHelper testOutput)
: base(testOutput) { }
[Fact(Timeout = 15000)] [Fact(Timeout = 15000)]
public async Task I_can_configure_the_command_to_listen_to_the_interrupt_signal() public async Task I_can_configure_the_command_to_listen_to_the_interrupt_signal()
{ {

View File

@@ -12,15 +12,15 @@
<PackageReference Include="Basic.Reference.Assemblies.Net70" Version="1.4.5" /> <PackageReference Include="Basic.Reference.Assemblies.Net70" Version="1.4.5" />
<PackageReference Include="CliWrap" Version="3.6.4" /> <PackageReference Include="CliWrap" Version="3.6.4" />
<PackageReference Include="coverlet.collector" Version="6.0.0" PrivateAssets="all" /> <PackageReference Include="coverlet.collector" Version="6.0.0" PrivateAssets="all" />
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" /> <PackageReference Include="CSharpier.MsBuild" Version="0.26.5" PrivateAssets="all" />
<PackageReference Include="FluentAssertions" Version="6.12.0" /> <PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="GitHubActionsTestLogger" Version="2.3.3" PrivateAssets="all" /> <PackageReference Include="GitHubActionsTestLogger" Version="2.3.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="PolyShim" Version="1.8.0" PrivateAssets="all" /> <PackageReference Include="PolyShim" Version="1.8.0" PrivateAssets="all" />
<PackageReference Include="xunit" Version="2.6.1" /> <PackageReference Include="xunit" Version="2.6.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" PrivateAssets="all" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.5.5" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -14,11 +14,8 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public class ConsoleSpecs : SpecsBase public class ConsoleSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
{ {
public ConsoleSpecs(ITestOutputHelper testOutput)
: base(testOutput) { }
[Fact(Timeout = 15000)] [Fact(Timeout = 15000)]
public async Task I_can_run_the_application_with_the_default_console_implementation_to_interact_with_the_system_console() public async Task I_can_run_the_application_with_the_default_console_implementation_to_interact_with_the_system_console()
{ {

View File

@@ -8,11 +8,8 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public class ConversionSpecs : SpecsBase public class ConversionSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
{ {
public ConversionSpecs(ITestOutputHelper testOutput)
: base(testOutput) { }
[Fact] [Fact]
public async Task I_can_bind_a_parameter_or_an_option_to_a_string_property() public async Task I_can_bind_a_parameter_or_an_option_to_a_string_property()
{ {

View File

@@ -11,11 +11,8 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public class DirectivesSpecs : SpecsBase public class DirectivesSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
{ {
public DirectivesSpecs(ITestOutputHelper testOutput)
: base(testOutput) { }
[Fact(Timeout = 15000)] [Fact(Timeout = 15000)]
public async Task I_can_use_the_debug_directive_to_make_the_application_wait_for_the_debugger_to_attach() public async Task I_can_use_the_debug_directive_to_make_the_application_wait_for_the_debugger_to_attach()
{ {

View File

@@ -12,11 +12,8 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public class EnvironmentSpecs : SpecsBase public class EnvironmentSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
{ {
public EnvironmentSpecs(ITestOutputHelper testOutput)
: base(testOutput) { }
[Fact] [Fact]
public async Task I_can_configure_an_option_to_fall_back_to_an_environment_variable_if_the_user_does_not_provide_the_corresponding_argument() public async Task I_can_configure_an_option_to_fall_back_to_an_environment_variable_if_the_user_does_not_provide_the_corresponding_argument()
{ {

View File

@@ -9,11 +9,8 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public class ErrorReportingSpecs : SpecsBase public class ErrorReportingSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
{ {
public ErrorReportingSpecs(ITestOutputHelper testOutput)
: base(testOutput) { }
[Fact] [Fact]
public async Task I_can_throw_an_exception_in_a_command_to_report_an_error_with_a_stacktrace() public async Task I_can_throw_an_exception_in_a_command_to_report_an_error_with_a_stacktrace()
{ {

View File

@@ -9,11 +9,8 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public class HelpTextSpecs : SpecsBase public class HelpTextSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
{ {
public HelpTextSpecs(ITestOutputHelper testOutput)
: base(testOutput) { }
[Fact] [Fact]
public async Task I_can_request_the_help_text_by_running_the_application_without_arguments_if_the_default_command_is_not_defined() public async Task I_can_request_the_help_text_by_running_the_application_without_arguments_if_the_default_command_is_not_defined()
{ {

View File

@@ -9,11 +9,8 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public class OptionBindingSpecs : SpecsBase public class OptionBindingSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
{ {
public OptionBindingSpecs(ITestOutputHelper testOutput)
: base(testOutput) { }
[Fact] [Fact]
public async Task I_can_bind_an_option_to_a_property_and_get_the_value_from_the_corresponding_argument_by_name() public async Task I_can_bind_an_option_to_a_property_and_get_the_value_from_the_corresponding_argument_by_name()
{ {

View File

@@ -8,11 +8,8 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public class ParameterBindingSpecs : SpecsBase public class ParameterBindingSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
{ {
public ParameterBindingSpecs(ITestOutputHelper testOutput)
: base(testOutput) { }
[Fact] [Fact]
public async Task I_can_bind_a_parameter_to_a_property_and_get_the_value_from_the_corresponding_argument() public async Task I_can_bind_a_parameter_to_a_property_and_get_the_value_from_the_corresponding_argument()
{ {

View File

@@ -8,11 +8,8 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public class RoutingSpecs : SpecsBase public class RoutingSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
{ {
public RoutingSpecs(ITestOutputHelper testOutput)
: base(testOutput) { }
[Fact] [Fact]
public async Task I_can_configure_a_command_to_be_executed_by_default_when_the_user_does_not_specify_a_command_name() public async Task I_can_configure_a_command_to_be_executed_by_default_when_the_user_does_not_specify_a_command_name()
{ {

View File

@@ -5,14 +5,12 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public abstract class SpecsBase : IDisposable public abstract class SpecsBase(ITestOutputHelper testOutput) : IDisposable
{ {
public ITestOutputHelper TestOutput { get; } public ITestOutputHelper TestOutput { get; } = testOutput;
public FakeInMemoryConsole FakeConsole { get; } = new(); public FakeInMemoryConsole FakeConsole { get; } = new();
protected SpecsBase(ITestOutputHelper testOutput) => TestOutput = testOutput;
public void Dispose() public void Dispose()
{ {
FakeConsole.DumpToTestOutput(TestOutput); FakeConsole.DumpToTestOutput(TestOutput);

View File

@@ -10,11 +10,8 @@ using Xunit.Abstractions;
namespace CliFx.Tests; namespace CliFx.Tests;
public class TypeActivationSpecs : SpecsBase public class TypeActivationSpecs(ITestOutputHelper testOutput) : SpecsBase(testOutput)
{ {
public TypeActivationSpecs(ITestOutputHelper testOutput)
: base(testOutput) { }
[Fact] [Fact]
public async Task I_can_configure_the_application_to_use_the_default_type_activator_to_initialize_types_through_parameterless_constructors() public async Task I_can_configure_the_application_to_use_the_default_type_activator_to_initialize_types_through_parameterless_constructors()
{ {

View File

@@ -21,7 +21,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" /> <PackageReference Include="CSharpier.MsBuild" Version="0.26.5" PrivateAssets="all" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="all" /> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="all" />
<PackageReference Include="PolyShim" Version="1.8.0" PrivateAssets="all" /> <PackageReference Include="PolyShim" Version="1.8.0" PrivateAssets="all" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" Condition="'$(TargetFramework)' == 'netstandard2.0'" /> <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" Condition="'$(TargetFramework)' == 'netstandard2.0'" />

View File

@@ -12,22 +12,16 @@ using CliFx.Utils.Extensions;
namespace CliFx; namespace CliFx;
internal class CommandBinder internal class CommandBinder(ITypeActivator typeActivator)
{ {
private readonly ITypeActivator _typeActivator;
private readonly IFormatProvider _formatProvider = CultureInfo.InvariantCulture; private readonly IFormatProvider _formatProvider = CultureInfo.InvariantCulture;
public CommandBinder(ITypeActivator typeActivator)
{
_typeActivator = typeActivator;
}
private object? ConvertSingle(IMemberSchema memberSchema, string? rawValue, Type targetType) private object? ConvertSingle(IMemberSchema memberSchema, string? rawValue, Type targetType)
{ {
// Custom converter // Custom converter
if (memberSchema.ConverterType is not null) if (memberSchema.ConverterType is not null)
{ {
var converter = _typeActivator.CreateInstance<IBindingConverter>( var converter = typeActivator.CreateInstance<IBindingConverter>(
memberSchema.ConverterType memberSchema.ConverterType
); );
return converter.Convert(rawValue); return converter.Convert(rawValue);
@@ -213,7 +207,7 @@ internal class CommandBinder
foreach (var validatorType in memberSchema.ValidatorTypes) foreach (var validatorType in memberSchema.ValidatorTypes)
{ {
var validator = _typeActivator.CreateInstance<IBindingValidator>(validatorType); var validator = typeActivator.CreateInstance<IBindingValidator>(validatorType);
var error = validator.Validate(convertedValue); var error = validator.Validate(convertedValue);
if (error is not null) if (error is not null)

View File

@@ -4,11 +4,9 @@ using CliFx.Input;
namespace CliFx.Formatting; namespace CliFx.Formatting;
internal class CommandInputConsoleFormatter : ConsoleFormatter internal class CommandInputConsoleFormatter(ConsoleWriter consoleWriter)
: ConsoleFormatter(consoleWriter)
{ {
public CommandInputConsoleFormatter(ConsoleWriter consoleWriter)
: base(consoleWriter) { }
private void WriteCommandLineArguments(CommandInput commandInput) private void WriteCommandLineArguments(CommandInput commandInput)
{ {
Write("Command-line:"); Write("Command-line:");

View File

@@ -3,50 +3,46 @@ using CliFx.Infrastructure;
namespace CliFx.Formatting; namespace CliFx.Formatting;
internal class ConsoleFormatter internal class ConsoleFormatter(ConsoleWriter consoleWriter)
{ {
private readonly ConsoleWriter _consoleWriter;
private int _column; private int _column;
private int _row; private int _row;
public bool IsEmpty => _column == 0 && _row == 0; public bool IsEmpty => _column == 0 && _row == 0;
public ConsoleFormatter(ConsoleWriter consoleWriter) => _consoleWriter = consoleWriter;
public void Write(string? value) public void Write(string? value)
{ {
_consoleWriter.Write(value); consoleWriter.Write(value);
_column += value?.Length ?? 0; _column += value?.Length ?? 0;
} }
public void Write(char value) public void Write(char value)
{ {
_consoleWriter.Write(value); consoleWriter.Write(value);
_column++; _column++;
} }
public void Write(ConsoleColor foregroundColor, string? value) public void Write(ConsoleColor foregroundColor, string? value)
{ {
using (_consoleWriter.Console.WithForegroundColor(foregroundColor)) using (consoleWriter.Console.WithForegroundColor(foregroundColor))
Write(value); Write(value);
} }
public void Write(ConsoleColor foregroundColor, char value) public void Write(ConsoleColor foregroundColor, char value)
{ {
using (_consoleWriter.Console.WithForegroundColor(foregroundColor)) using (consoleWriter.Console.WithForegroundColor(foregroundColor))
Write(value); Write(value);
} }
public void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string? value) public void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string? value)
{ {
using (_consoleWriter.Console.WithColors(foregroundColor, backgroundColor)) using (consoleWriter.Console.WithColors(foregroundColor, backgroundColor))
Write(value); Write(value);
} }
public void WriteLine() public void WriteLine()
{ {
_consoleWriter.WriteLine(); consoleWriter.WriteLine();
_column = 0; _column = 0;
_row++; _row++;
} }

View File

@@ -7,11 +7,9 @@ using CliFx.Utils.Extensions;
namespace CliFx.Formatting; namespace CliFx.Formatting;
internal class ExceptionConsoleFormatter : ConsoleFormatter internal class ExceptionConsoleFormatter(ConsoleWriter consoleWriter)
: ConsoleFormatter(consoleWriter)
{ {
public ExceptionConsoleFormatter(ConsoleWriter consoleWriter)
: base(consoleWriter) { }
private void WriteStackFrame(StackFrame stackFrame, int indentLevel) private void WriteStackFrame(StackFrame stackFrame, int indentLevel)
{ {
WriteHorizontalMargin(2 + 4 * indentLevel); WriteHorizontalMargin(2 + 4 * indentLevel);

View File

@@ -9,16 +9,9 @@ using CliFx.Utils.Extensions;
namespace CliFx.Formatting; namespace CliFx.Formatting;
internal class HelpConsoleFormatter : ConsoleFormatter internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext context)
: ConsoleFormatter(consoleWriter)
{ {
private readonly HelpContext _context;
public HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext context)
: base(consoleWriter)
{
_context = context;
}
private void WriteHeader(string text) private void WriteHeader(string text)
{ {
Write(ConsoleColor.White, text.ToUpperInvariant()); Write(ConsoleColor.White, text.ToUpperInvariant());
@@ -27,13 +20,13 @@ internal class HelpConsoleFormatter : ConsoleFormatter
private void WriteCommandInvocation() private void WriteCommandInvocation()
{ {
Write(_context.ApplicationMetadata.ExecutableName); Write(context.ApplicationMetadata.ExecutableName);
// Command name // Command name
if (!string.IsNullOrWhiteSpace(_context.CommandSchema.Name)) if (!string.IsNullOrWhiteSpace(context.CommandSchema.Name))
{ {
Write(' '); Write(' ');
Write(ConsoleColor.Cyan, _context.CommandSchema.Name); Write(ConsoleColor.Cyan, context.CommandSchema.Name);
} }
} }
@@ -43,16 +36,16 @@ internal class HelpConsoleFormatter : ConsoleFormatter
WriteVerticalMargin(); WriteVerticalMargin();
// Title and version // Title and version
Write(ConsoleColor.White, _context.ApplicationMetadata.Title); Write(ConsoleColor.White, context.ApplicationMetadata.Title);
Write(' '); Write(' ');
Write(ConsoleColor.Yellow, _context.ApplicationMetadata.Version); Write(ConsoleColor.Yellow, context.ApplicationMetadata.Version);
WriteLine(); WriteLine();
// Description // Description
if (!string.IsNullOrWhiteSpace(_context.ApplicationMetadata.Description)) if (!string.IsNullOrWhiteSpace(context.ApplicationMetadata.Description))
{ {
WriteHorizontalMargin(); WriteHorizontalMargin();
Write(_context.ApplicationMetadata.Description); Write(context.ApplicationMetadata.Description);
WriteLine(); WriteLine();
} }
} }
@@ -72,7 +65,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
Write(' '); Write(' ');
// Parameters // Parameters
foreach (var parameter in _context.CommandSchema.Parameters.OrderBy(p => p.Order)) foreach (var parameter in context.CommandSchema.Parameters.OrderBy(p => p.Order))
{ {
Write( Write(
ConsoleColor.DarkCyan, ConsoleColor.DarkCyan,
@@ -82,7 +75,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
} }
// Required options // Required options
foreach (var option in _context.CommandSchema.Options.Where(o => o.IsRequired)) foreach (var option in context.CommandSchema.Options.Where(o => o.IsRequired))
{ {
Write( Write(
ConsoleColor.Yellow, ConsoleColor.Yellow,
@@ -97,7 +90,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
} }
// Placeholder for non-required options // Placeholder for non-required options
if (_context.CommandSchema.Options.Any(o => !o.IsRequired)) if (context.CommandSchema.Options.Any(o => !o.IsRequired))
{ {
Write(ConsoleColor.Yellow, "[options]"); Write(ConsoleColor.Yellow, "[options]");
} }
@@ -106,9 +99,9 @@ internal class HelpConsoleFormatter : ConsoleFormatter
} }
// Child command usage // Child command usage
var childCommandSchemas = _context var childCommandSchemas = context
.ApplicationSchema .ApplicationSchema
.GetChildCommands(_context.CommandSchema.Name); .GetChildCommands(context.CommandSchema.Name);
if (childCommandSchemas.Any()) if (childCommandSchemas.Any())
{ {
@@ -130,7 +123,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
private void WriteCommandDescription() private void WriteCommandDescription()
{ {
if (string.IsNullOrWhiteSpace(_context.CommandSchema.Description)) if (string.IsNullOrWhiteSpace(context.CommandSchema.Description))
return; return;
if (!IsEmpty) if (!IsEmpty)
@@ -140,13 +133,13 @@ internal class HelpConsoleFormatter : ConsoleFormatter
WriteHorizontalMargin(); WriteHorizontalMargin();
Write(_context.CommandSchema.Description); Write(context.CommandSchema.Description);
WriteLine(); WriteLine();
} }
private void WriteCommandParameters() private void WriteCommandParameters()
{ {
if (!_context.CommandSchema.Parameters.Any()) if (!context.CommandSchema.Parameters.Any())
return; return;
if (!IsEmpty) if (!IsEmpty)
@@ -154,7 +147,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
WriteHeader("Parameters"); WriteHeader("Parameters");
foreach (var parameterSchema in _context.CommandSchema.Parameters.OrderBy(p => p.Order)) foreach (var parameterSchema in context.CommandSchema.Parameters.OrderBy(p => p.Order))
{ {
if (parameterSchema.IsRequired) if (parameterSchema.IsRequired)
{ {
@@ -224,7 +217,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
WriteHeader("Options"); WriteHeader("Options");
foreach ( foreach (
var optionSchema in _context.CommandSchema.Options.OrderByDescending(o => o.IsRequired) var optionSchema in context.CommandSchema.Options.OrderByDescending(o => o.IsRequired)
) )
{ {
if (optionSchema.IsRequired) if (optionSchema.IsRequired)
@@ -314,7 +307,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
private void WriteDefaultValue(IMemberSchema schema) private void WriteDefaultValue(IMemberSchema schema)
{ {
var defaultValue = _context.CommandDefaultValues.GetValueOrDefault(schema); var defaultValue = context.CommandDefaultValues.GetValueOrDefault(schema);
if (defaultValue is not null) if (defaultValue is not null)
{ {
// Non-Scalar // Non-Scalar
@@ -365,9 +358,9 @@ internal class HelpConsoleFormatter : ConsoleFormatter
private void WriteCommandChildren() private void WriteCommandChildren()
{ {
var childCommandSchemas = _context var childCommandSchemas = context
.ApplicationSchema .ApplicationSchema
.GetChildCommands(_context.CommandSchema.Name) .GetChildCommands(context.CommandSchema.Name)
.OrderBy(a => a.Name, StringComparer.Ordinal) .OrderBy(a => a.Name, StringComparer.Ordinal)
.ToArray(); .ToArray();
@@ -386,7 +379,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
Write( Write(
ConsoleColor.Cyan, ConsoleColor.Cyan,
// Relative to current command // Relative to current command
childCommandSchema.Name?.Substring(_context.CommandSchema.Name?.Length ?? 0).Trim() childCommandSchema.Name?.Substring(context.CommandSchema.Name?.Length ?? 0).Trim()
); );
WriteColumnMargin(); WriteColumnMargin();
@@ -399,7 +392,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
} }
// Child commands of child command // Child commands of child command
var grandChildCommandSchemas = _context var grandChildCommandSchemas = context
.ApplicationSchema .ApplicationSchema
.GetChildCommands(childCommandSchema.Name) .GetChildCommands(childCommandSchema.Name)
.OrderBy(c => c.Name, StringComparer.Ordinal) .OrderBy(c => c.Name, StringComparer.Ordinal)
@@ -426,7 +419,7 @@ internal class HelpConsoleFormatter : ConsoleFormatter
// Relative to current command (not the parent) // Relative to current command (not the parent)
grandChildCommandSchema grandChildCommandSchema
.Name .Name
?.Substring(_context.CommandSchema.Name?.Length ?? 0) ?.Substring(context.CommandSchema.Name?.Length ?? 0)
.Trim() .Trim()
); );
} }

View File

@@ -3,26 +3,19 @@ using CliFx.Schema;
namespace CliFx.Formatting; namespace CliFx.Formatting;
internal class HelpContext internal class HelpContext(
{
public ApplicationMetadata ApplicationMetadata { get; }
public ApplicationSchema ApplicationSchema { get; }
public CommandSchema CommandSchema { get; }
public IReadOnlyDictionary<IMemberSchema, object?> CommandDefaultValues { get; }
public HelpContext(
ApplicationMetadata applicationMetadata, ApplicationMetadata applicationMetadata,
ApplicationSchema applicationSchema, ApplicationSchema applicationSchema,
CommandSchema commandSchema, CommandSchema commandSchema,
IReadOnlyDictionary<IMemberSchema, object?> commandDefaultValues IReadOnlyDictionary<IMemberSchema, object?> commandDefaultValues
) )
{ {
ApplicationMetadata = applicationMetadata; public ApplicationMetadata ApplicationMetadata { get; } = applicationMetadata;
ApplicationSchema = applicationSchema;
CommandSchema = commandSchema; public ApplicationSchema ApplicationSchema { get; } = applicationSchema;
CommandDefaultValues = commandDefaultValues;
} public CommandSchema CommandSchema { get; } = commandSchema;
public IReadOnlyDictionary<IMemberSchema, object?> CommandDefaultValues { get; } =
commandDefaultValues;
} }

View File

@@ -5,17 +5,24 @@ using CliFx.Utils.Extensions;
namespace CliFx.Input; namespace CliFx.Input;
internal partial class CommandInput internal partial class CommandInput(
string? commandName,
IReadOnlyList<DirectiveInput> directives,
IReadOnlyList<ParameterInput> parameters,
IReadOnlyList<OptionInput> options,
IReadOnlyList<EnvironmentVariableInput> environmentVariables
)
{ {
public string? CommandName { get; } public string? CommandName { get; } = commandName;
public IReadOnlyList<DirectiveInput> Directives { get; } public IReadOnlyList<DirectiveInput> Directives { get; } = directives;
public IReadOnlyList<ParameterInput> Parameters { get; } public IReadOnlyList<ParameterInput> Parameters { get; } = parameters;
public IReadOnlyList<OptionInput> Options { get; } public IReadOnlyList<OptionInput> Options { get; } = options;
public IReadOnlyList<EnvironmentVariableInput> EnvironmentVariables { get; } public IReadOnlyList<EnvironmentVariableInput> EnvironmentVariables { get; } =
environmentVariables;
public bool HasArguments => public bool HasArguments =>
!string.IsNullOrWhiteSpace(CommandName) !string.IsNullOrWhiteSpace(CommandName)
@@ -30,21 +37,6 @@ internal partial class CommandInput
public bool IsHelpOptionSpecified => Options.Any(o => o.IsHelpOption); public bool IsHelpOptionSpecified => Options.Any(o => o.IsHelpOption);
public bool IsVersionOptionSpecified => Options.Any(o => o.IsVersionOption); public bool IsVersionOptionSpecified => Options.Any(o => o.IsVersionOption);
public CommandInput(
string? commandName,
IReadOnlyList<DirectiveInput> directives,
IReadOnlyList<ParameterInput> parameters,
IReadOnlyList<OptionInput> options,
IReadOnlyList<EnvironmentVariableInput> environmentVariables
)
{
CommandName = commandName;
Directives = directives;
Parameters = parameters;
Options = options;
EnvironmentVariables = environmentVariables;
}
} }
internal partial class CommandInput internal partial class CommandInput

View File

@@ -2,15 +2,13 @@
namespace CliFx.Input; namespace CliFx.Input;
internal class DirectiveInput internal class DirectiveInput(string name)
{ {
public string Name { get; } public string Name { get; } = name;
public bool IsDebugDirective => public bool IsDebugDirective =>
string.Equals(Name, "debug", StringComparison.OrdinalIgnoreCase); string.Equals(Name, "debug", StringComparison.OrdinalIgnoreCase);
public bool IsPreviewDirective => public bool IsPreviewDirective =>
string.Equals(Name, "preview", StringComparison.OrdinalIgnoreCase); string.Equals(Name, "preview", StringComparison.OrdinalIgnoreCase);
public DirectiveInput(string name) => Name = name;
} }

View File

@@ -3,17 +3,11 @@ using System.IO;
namespace CliFx.Input; namespace CliFx.Input;
internal class EnvironmentVariableInput internal class EnvironmentVariableInput(string name, string value)
{ {
public string Name { get; } public string Name { get; } = name;
public string Value { get; } public string Value { get; } = value;
public EnvironmentVariableInput(string name, string value)
{
Name = name;
Value = value;
}
public IReadOnlyList<string> SplitValues() => Value.Split(Path.PathSeparator); public IReadOnlyList<string> SplitValues() => Value.Split(Path.PathSeparator);
} }

View File

@@ -3,22 +3,16 @@ using CliFx.Schema;
namespace CliFx.Input; namespace CliFx.Input;
internal class OptionInput internal class OptionInput(string identifier, IReadOnlyList<string> values)
{ {
public string Identifier { get; } public string Identifier { get; } = identifier;
public IReadOnlyList<string> Values { get; } public IReadOnlyList<string> Values { get; } = values;
public bool IsHelpOption => OptionSchema.HelpOption.MatchesIdentifier(Identifier); public bool IsHelpOption => OptionSchema.HelpOption.MatchesIdentifier(Identifier);
public bool IsVersionOption => OptionSchema.VersionOption.MatchesIdentifier(Identifier); public bool IsVersionOption => OptionSchema.VersionOption.MatchesIdentifier(Identifier);
public OptionInput(string identifier, IReadOnlyList<string> values)
{
Identifier = identifier;
Values = values;
}
public string GetFormattedIdentifier() => public string GetFormattedIdentifier() =>
Identifier switch Identifier switch
{ {

View File

@@ -1,10 +1,8 @@
namespace CliFx.Input; namespace CliFx.Input;
internal class ParameterInput internal class ParameterInput(string value)
{ {
public string Value { get; } public string Value { get; } = value;
public ParameterInput(string value) => Value = value;
public string GetFormattedIdentifier() => $"<{Value}>"; public string GetFormattedIdentifier() => $"<{Value}>";
} }

View File

@@ -5,14 +5,9 @@ using CliFx.Utils.Extensions;
namespace CliFx.Schema; namespace CliFx.Schema;
internal partial class ApplicationSchema internal partial class ApplicationSchema(IReadOnlyList<CommandSchema> commands)
{ {
public IReadOnlyList<CommandSchema> Commands { get; } public IReadOnlyList<CommandSchema> Commands { get; } = commands;
public ApplicationSchema(IReadOnlyList<CommandSchema> commands)
{
Commands = commands;
}
public IReadOnlyList<string> GetCommandNames() => public IReadOnlyList<string> GetCommandNames() =>
Commands.Select(c => c.Name).WhereNotNullOrWhiteSpace().ToArray(); Commands.Select(c => c.Name).WhereNotNullOrWhiteSpace().ToArray();

View File

@@ -5,18 +5,14 @@ using CliFx.Utils.Extensions;
namespace CliFx.Schema; namespace CliFx.Schema;
internal class BindablePropertyDescriptor : IPropertyDescriptor internal class BindablePropertyDescriptor(PropertyInfo property) : IPropertyDescriptor
{ {
private readonly PropertyInfo _property; public Type Type => property.PropertyType;
public Type Type => _property.PropertyType; public object? GetValue(ICommand commandInstance) => property.GetValue(commandInstance);
public BindablePropertyDescriptor(PropertyInfo property) => _property = property;
public object? GetValue(ICommand commandInstance) => _property.GetValue(commandInstance);
public void SetValue(ICommand commandInstance, object? value) => public void SetValue(ICommand commandInstance, object? value) =>
_property.SetValue(commandInstance, value); property.SetValue(commandInstance, value);
public IReadOnlyList<object?> GetValidValues() public IReadOnlyList<object?> GetValidValues()
{ {

View File

@@ -8,25 +8,7 @@ using CliFx.Utils.Extensions;
namespace CliFx.Schema; namespace CliFx.Schema;
internal partial class CommandSchema internal partial class CommandSchema(
{
public Type Type { get; }
public string? Name { get; }
public string? Description { get; }
public IReadOnlyList<ParameterSchema> Parameters { get; }
public IReadOnlyList<OptionSchema> Options { get; }
public bool IsDefault => string.IsNullOrWhiteSpace(Name);
public bool IsHelpOptionAvailable => Options.Contains(OptionSchema.HelpOption);
public bool IsVersionOptionAvailable => Options.Contains(OptionSchema.VersionOption);
public CommandSchema(
Type type, Type type,
string? name, string? name,
string? description, string? description,
@@ -34,12 +16,21 @@ internal partial class CommandSchema
IReadOnlyList<OptionSchema> options IReadOnlyList<OptionSchema> options
) )
{ {
Type = type; public Type Type { get; } = type;
Name = name;
Description = description; public string? Name { get; } = name;
Parameters = parameters;
Options = options; public string? Description { get; } = description;
}
public IReadOnlyList<ParameterSchema> Parameters { get; } = parameters;
public IReadOnlyList<OptionSchema> Options { get; } = options;
public bool IsDefault => string.IsNullOrWhiteSpace(Name);
public bool IsHelpOptionAvailable => Options.Contains(OptionSchema.HelpOption);
public bool IsVersionOptionAvailable => Options.Contains(OptionSchema.VersionOption);
public bool MatchesName(string? name) => public bool MatchesName(string? name) =>
!string.IsNullOrWhiteSpace(Name) !string.IsNullOrWhiteSpace(Name)

View File

@@ -7,25 +7,7 @@ using CliFx.Utils.Extensions;
namespace CliFx.Schema; namespace CliFx.Schema;
internal partial class OptionSchema : IMemberSchema internal partial class OptionSchema(
{
public IPropertyDescriptor Property { get; }
public string? Name { get; }
public char? ShortName { get; }
public string? EnvironmentVariable { get; }
public bool IsRequired { get; }
public string? Description { get; }
public Type? ConverterType { get; }
public IReadOnlyList<Type> ValidatorTypes { get; }
public OptionSchema(
IPropertyDescriptor property, IPropertyDescriptor property,
string? name, string? name,
char? shortName, char? shortName,
@@ -34,17 +16,23 @@ internal partial class OptionSchema : IMemberSchema
string? description, string? description,
Type? converterType, Type? converterType,
IReadOnlyList<Type> validatorTypes IReadOnlyList<Type> validatorTypes
) ) : IMemberSchema
{ {
Property = property; public IPropertyDescriptor Property { get; } = property;
Name = name;
ShortName = shortName; public string? Name { get; } = name;
EnvironmentVariable = environmentVariable;
IsRequired = isRequired; public char? ShortName { get; } = shortName;
Description = description;
ConverterType = converterType; public string? EnvironmentVariable { get; } = environmentVariable;
ValidatorTypes = validatorTypes;
} public bool IsRequired { get; } = isRequired;
public string? Description { get; } = description;
public Type? ConverterType { get; } = converterType;
public IReadOnlyList<Type> ValidatorTypes { get; } = validatorTypes;
public bool MatchesName(string? name) => public bool MatchesName(string? name) =>
!string.IsNullOrWhiteSpace(Name) !string.IsNullOrWhiteSpace(Name)

View File

@@ -6,23 +6,7 @@ using CliFx.Utils.Extensions;
namespace CliFx.Schema; namespace CliFx.Schema;
internal partial class ParameterSchema : IMemberSchema internal partial class ParameterSchema(
{
public IPropertyDescriptor Property { get; }
public int Order { get; }
public string Name { get; }
public bool IsRequired { get; }
public string? Description { get; }
public Type? ConverterType { get; }
public IReadOnlyList<Type> ValidatorTypes { get; }
public ParameterSchema(
IPropertyDescriptor property, IPropertyDescriptor property,
int order, int order,
string name, string name,
@@ -30,16 +14,21 @@ internal partial class ParameterSchema : IMemberSchema
string? description, string? description,
Type? converterType, Type? converterType,
IReadOnlyList<Type> validatorTypes IReadOnlyList<Type> validatorTypes
) ) : IMemberSchema
{ {
Property = property; public IPropertyDescriptor Property { get; } = property;
Order = order;
Name = name; public int Order { get; } = order;
IsRequired = isRequired;
Description = description; public string Name { get; } = name;
ConverterType = converterType;
ValidatorTypes = validatorTypes; public bool IsRequired { get; } = isRequired;
}
public string? Description { get; } = description;
public Type? ConverterType { get; } = converterType;
public IReadOnlyList<Type> ValidatorTypes { get; } = validatorTypes;
public string GetFormattedIdentifier() => Property.IsScalar() ? $"<{Name}>" : $"<{Name}...>"; public string GetFormattedIdentifier() => Property.IsScalar() ? $"<{Name}>" : $"<{Name}...>";
} }

View File

@@ -3,13 +3,9 @@ using System.Collections.Generic;
namespace CliFx.Utils; namespace CliFx.Utils;
internal partial class Disposable : IDisposable internal partial class Disposable(Action dispose) : IDisposable
{ {
private readonly Action _dispose; public void Dispose() => dispose();
public Disposable(Action dispose) => _dispose = dispose;
public void Dispose() => _dispose();
} }
internal partial class Disposable internal partial class Disposable

View File

@@ -10,65 +10,58 @@ namespace CliFx.Utils;
// https://source.dot.net/#System.Console/ConsoleEncoding.cs,5eedd083a4a4f4a2 // https://source.dot.net/#System.Console/ConsoleEncoding.cs,5eedd083a4a4f4a2
// Majority of overrides are just proxy calls to avoid potentially more expensive base behavior. // Majority of overrides are just proxy calls to avoid potentially more expensive base behavior.
// The important part is the GetPreamble() method that has been overriden to return an empty array. // The important part is the GetPreamble() method that has been overriden to return an empty array.
internal class NoPreambleEncoding : Encoding internal class NoPreambleEncoding(Encoding underlyingEncoding)
{ : Encoding(
private readonly Encoding _underlyingEncoding;
[ExcludeFromCodeCoverage]
public override string EncodingName => _underlyingEncoding.EncodingName;
[ExcludeFromCodeCoverage]
public override string BodyName => _underlyingEncoding.BodyName;
[ExcludeFromCodeCoverage]
public override int CodePage => _underlyingEncoding.CodePage;
[ExcludeFromCodeCoverage]
public override int WindowsCodePage => _underlyingEncoding.WindowsCodePage;
[ExcludeFromCodeCoverage]
public override string HeaderName => _underlyingEncoding.HeaderName;
[ExcludeFromCodeCoverage]
public override string WebName => _underlyingEncoding.WebName;
[ExcludeFromCodeCoverage]
public override bool IsBrowserDisplay => _underlyingEncoding.IsBrowserDisplay;
[ExcludeFromCodeCoverage]
public override bool IsBrowserSave => _underlyingEncoding.IsBrowserSave;
[ExcludeFromCodeCoverage]
public override bool IsSingleByte => _underlyingEncoding.IsSingleByte;
[ExcludeFromCodeCoverage]
public override bool IsMailNewsDisplay => _underlyingEncoding.IsMailNewsDisplay;
[ExcludeFromCodeCoverage]
public override bool IsMailNewsSave => _underlyingEncoding.IsMailNewsSave;
public NoPreambleEncoding(Encoding underlyingEncoding)
: base(
underlyingEncoding.CodePage, underlyingEncoding.CodePage,
underlyingEncoding.EncoderFallback, underlyingEncoding.EncoderFallback,
underlyingEncoding.DecoderFallback underlyingEncoding.DecoderFallback
) )
{ {
_underlyingEncoding = underlyingEncoding; [ExcludeFromCodeCoverage]
} public override string EncodingName => underlyingEncoding.EncodingName;
[ExcludeFromCodeCoverage]
public override string BodyName => underlyingEncoding.BodyName;
[ExcludeFromCodeCoverage]
public override int CodePage => underlyingEncoding.CodePage;
[ExcludeFromCodeCoverage]
public override int WindowsCodePage => underlyingEncoding.WindowsCodePage;
[ExcludeFromCodeCoverage]
public override string HeaderName => underlyingEncoding.HeaderName;
[ExcludeFromCodeCoverage]
public override string WebName => underlyingEncoding.WebName;
[ExcludeFromCodeCoverage]
public override bool IsBrowserDisplay => underlyingEncoding.IsBrowserDisplay;
[ExcludeFromCodeCoverage]
public override bool IsBrowserSave => underlyingEncoding.IsBrowserSave;
[ExcludeFromCodeCoverage]
public override bool IsSingleByte => underlyingEncoding.IsSingleByte;
[ExcludeFromCodeCoverage]
public override bool IsMailNewsDisplay => underlyingEncoding.IsMailNewsDisplay;
[ExcludeFromCodeCoverage]
public override bool IsMailNewsSave => underlyingEncoding.IsMailNewsSave;
// This is the only part that changes // This is the only part that changes
public override byte[] GetPreamble() => Array.Empty<byte>(); public override byte[] GetPreamble() => Array.Empty<byte>();
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override int GetByteCount(char[] chars, int index, int count) => public override int GetByteCount(char[] chars, int index, int count) =>
_underlyingEncoding.GetByteCount(chars, index, count); underlyingEncoding.GetByteCount(chars, index, count);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override int GetByteCount(char[] chars) => _underlyingEncoding.GetByteCount(chars); public override int GetByteCount(char[] chars) => underlyingEncoding.GetByteCount(chars);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override int GetByteCount(string s) => _underlyingEncoding.GetByteCount(s); public override int GetByteCount(string s) => underlyingEncoding.GetByteCount(s);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override int GetBytes( public override int GetBytes(
@@ -77,14 +70,14 @@ internal class NoPreambleEncoding : Encoding
int charCount, int charCount,
byte[] bytes, byte[] bytes,
int byteIndex int byteIndex
) => _underlyingEncoding.GetBytes(chars, charIndex, charCount, bytes, byteIndex); ) => underlyingEncoding.GetBytes(chars, charIndex, charCount, bytes, byteIndex);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override byte[] GetBytes(char[] chars, int index, int count) => public override byte[] GetBytes(char[] chars, int index, int count) =>
_underlyingEncoding.GetBytes(chars, index, count); underlyingEncoding.GetBytes(chars, index, count);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override byte[] GetBytes(char[] chars) => _underlyingEncoding.GetBytes(chars); public override byte[] GetBytes(char[] chars) => underlyingEncoding.GetBytes(chars);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override int GetBytes( public override int GetBytes(
@@ -93,17 +86,17 @@ internal class NoPreambleEncoding : Encoding
int charCount, int charCount,
byte[] bytes, byte[] bytes,
int byteIndex int byteIndex
) => _underlyingEncoding.GetBytes(s, charIndex, charCount, bytes, byteIndex); ) => underlyingEncoding.GetBytes(s, charIndex, charCount, bytes, byteIndex);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override byte[] GetBytes(string s) => _underlyingEncoding.GetBytes(s); public override byte[] GetBytes(string s) => underlyingEncoding.GetBytes(s);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override int GetCharCount(byte[] bytes, int index, int count) => public override int GetCharCount(byte[] bytes, int index, int count) =>
_underlyingEncoding.GetCharCount(bytes, index, count); underlyingEncoding.GetCharCount(bytes, index, count);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override int GetCharCount(byte[] bytes) => _underlyingEncoding.GetCharCount(bytes); public override int GetCharCount(byte[] bytes) => underlyingEncoding.GetCharCount(bytes);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override int GetChars( public override int GetChars(
@@ -112,39 +105,39 @@ internal class NoPreambleEncoding : Encoding
int byteCount, int byteCount,
char[] chars, char[] chars,
int charIndex int charIndex
) => _underlyingEncoding.GetChars(bytes, byteIndex, byteCount, chars, charIndex); ) => underlyingEncoding.GetChars(bytes, byteIndex, byteCount, chars, charIndex);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override char[] GetChars(byte[] bytes) => _underlyingEncoding.GetChars(bytes); public override char[] GetChars(byte[] bytes) => underlyingEncoding.GetChars(bytes);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override char[] GetChars(byte[] bytes, int index, int count) => public override char[] GetChars(byte[] bytes, int index, int count) =>
_underlyingEncoding.GetChars(bytes, index, count); underlyingEncoding.GetChars(bytes, index, count);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override string GetString(byte[] bytes) => _underlyingEncoding.GetString(bytes); public override string GetString(byte[] bytes) => underlyingEncoding.GetString(bytes);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override string GetString(byte[] bytes, int index, int count) => public override string GetString(byte[] bytes, int index, int count) =>
_underlyingEncoding.GetString(bytes, index, count); underlyingEncoding.GetString(bytes, index, count);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override int GetMaxByteCount(int charCount) => public override int GetMaxByteCount(int charCount) =>
_underlyingEncoding.GetMaxByteCount(charCount); underlyingEncoding.GetMaxByteCount(charCount);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override int GetMaxCharCount(int byteCount) => public override int GetMaxCharCount(int byteCount) =>
_underlyingEncoding.GetMaxCharCount(byteCount); underlyingEncoding.GetMaxCharCount(byteCount);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override bool IsAlwaysNormalized(NormalizationForm form) => public override bool IsAlwaysNormalized(NormalizationForm form) =>
_underlyingEncoding.IsAlwaysNormalized(form); underlyingEncoding.IsAlwaysNormalized(form);
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override Encoder GetEncoder() => _underlyingEncoding.GetEncoder(); public override Encoder GetEncoder() => underlyingEncoding.GetEncoder();
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override Decoder GetDecoder() => _underlyingEncoding.GetDecoder(); public override Decoder GetDecoder() => underlyingEncoding.GetDecoder();
[ExcludeFromCodeCoverage] [ExcludeFromCodeCoverage]
public override object Clone() => new NoPreambleEncoding((Encoding)base.Clone()); public override object Clone() => new NoPreambleEncoding((Encoding)base.Clone());

View File

@@ -6,32 +6,14 @@ using CliFx.Utils.Extensions;
namespace CliFx.Utils; namespace CliFx.Utils;
internal class StackFrameParameter internal class StackFrameParameter(string type, string? name)
{ {
public string Type { get; } public string Type { get; } = type;
public string? Name { get; } public string? Name { get; } = name;
public StackFrameParameter(string type, string? name)
{
Type = type;
Name = name;
}
} }
internal partial class StackFrame internal partial class StackFrame(
{
public string ParentType { get; }
public string MethodName { get; }
public IReadOnlyList<StackFrameParameter> Parameters { get; }
public string? FilePath { get; }
public string? LineNumber { get; }
public StackFrame(
string parentType, string parentType,
string methodName, string methodName,
IReadOnlyList<StackFrameParameter> parameters, IReadOnlyList<StackFrameParameter> parameters,
@@ -39,12 +21,15 @@ internal partial class StackFrame
string? lineNumber string? lineNumber
) )
{ {
ParentType = parentType; public string ParentType { get; } = parentType;
MethodName = methodName;
Parameters = parameters; public string MethodName { get; } = methodName;
FilePath = filePath;
LineNumber = lineNumber; public IReadOnlyList<StackFrameParameter> Parameters { get; } = parameters;
}
public string? FilePath { get; } = filePath;
public string? LineNumber { get; } = lineNumber;
} }
internal partial class StackFrame internal partial class StackFrame