From 3fc7054f801d295e0c5eb3193480869f8ab921ac Mon Sep 17 00:00:00 2001
From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
Date: Sun, 16 Jun 2024 01:31:34 +0300
Subject: [PATCH] asd
---
CliFx/Attributes/CommandAttribute.cs | 2 +-
.../Attributes/CommandHelpOptionAttribute.cs | 16 +++++++
CliFx/Attributes/CommandOptionAttribute.cs | 2 +-
CliFx/Attributes/CommandParameterAttribute.cs | 2 +-
.../CommandVersionOptionAttribute.cs | 16 +++++++
CliFx/CliApplication.cs | 48 +++++++++----------
CliFx/CliApplicationBuilder.cs | 2 +-
CliFx/CliFx.csproj | 2 +-
CliFx/CommandBinder.cs | 12 ++---
CliFx/FallbackDefaultCommand.cs | 8 +++-
CliFx/Formatting/HelpConsoleFormatter.cs | 14 +++---
CliFx/Formatting/HelpContext.cs | 4 +-
CliFx/ICommandWithHelpOption.cs | 12 +++++
CliFx/ICommandWithVersionOption.cs | 12 +++++
CliFx/Schema/CommandSchema.cs | 16 +++----
CliFx/Schema/IInputSchema.cs | 25 ----------
CliFx/Schema/InputSchema.cs | 35 ++++++++++++++
CliFx/Schema/OptionSchema.cs | 22 ++-------
CliFx/Schema/ParameterSchema.cs | 25 ++--------
CliFx/Schema/PropertyBinding.cs | 39 +++++++++------
CliFx/Utils/Extensions/TypeExtensions.cs | 29 +++++------
21 files changed, 195 insertions(+), 148 deletions(-)
create mode 100644 CliFx/Attributes/CommandHelpOptionAttribute.cs
create mode 100644 CliFx/Attributes/CommandVersionOptionAttribute.cs
create mode 100644 CliFx/ICommandWithHelpOption.cs
create mode 100644 CliFx/ICommandWithVersionOption.cs
delete mode 100644 CliFx/Schema/IInputSchema.cs
create mode 100644 CliFx/Schema/InputSchema.cs
diff --git a/CliFx/Attributes/CommandAttribute.cs b/CliFx/Attributes/CommandAttribute.cs
index 2066428..d7cbb1d 100644
--- a/CliFx/Attributes/CommandAttribute.cs
+++ b/CliFx/Attributes/CommandAttribute.cs
@@ -6,7 +6,7 @@ namespace CliFx.Attributes;
/// Annotates a type that defines a command.
///
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
-public sealed class CommandAttribute : Attribute
+public class CommandAttribute : Attribute
{
///
/// Initializes an instance of .
diff --git a/CliFx/Attributes/CommandHelpOptionAttribute.cs b/CliFx/Attributes/CommandHelpOptionAttribute.cs
new file mode 100644
index 0000000..2c7ba04
--- /dev/null
+++ b/CliFx/Attributes/CommandHelpOptionAttribute.cs
@@ -0,0 +1,16 @@
+namespace CliFx.Attributes;
+
+///
+/// Annotates a property that defines the help option for a command.
+///
+public class CommandHelpOptionAttribute : CommandOptionAttribute
+{
+ ///
+ /// Initializes an instance of .
+ ///
+ public CommandHelpOptionAttribute()
+ : base("help", 'h')
+ {
+ Description = "Show help for this command.";
+ }
+}
diff --git a/CliFx/Attributes/CommandOptionAttribute.cs b/CliFx/Attributes/CommandOptionAttribute.cs
index 517a56b..4cd8ea6 100644
--- a/CliFx/Attributes/CommandOptionAttribute.cs
+++ b/CliFx/Attributes/CommandOptionAttribute.cs
@@ -7,7 +7,7 @@ namespace CliFx.Attributes;
/// Annotates a property that defines a command option.
///
[AttributeUsage(AttributeTargets.Property)]
-public sealed class CommandOptionAttribute : Attribute
+public class CommandOptionAttribute : Attribute
{
///
/// Initializes an instance of .
diff --git a/CliFx/Attributes/CommandParameterAttribute.cs b/CliFx/Attributes/CommandParameterAttribute.cs
index 3314403..c377105 100644
--- a/CliFx/Attributes/CommandParameterAttribute.cs
+++ b/CliFx/Attributes/CommandParameterAttribute.cs
@@ -7,7 +7,7 @@ namespace CliFx.Attributes;
/// Annotates a property that defines a command parameter.
///
[AttributeUsage(AttributeTargets.Property)]
-public sealed class CommandParameterAttribute(int order) : Attribute
+public class CommandParameterAttribute(int order) : Attribute
{
///
/// Parameter order.
diff --git a/CliFx/Attributes/CommandVersionOptionAttribute.cs b/CliFx/Attributes/CommandVersionOptionAttribute.cs
new file mode 100644
index 0000000..44cce02
--- /dev/null
+++ b/CliFx/Attributes/CommandVersionOptionAttribute.cs
@@ -0,0 +1,16 @@
+namespace CliFx.Attributes;
+
+///
+/// Annotates a property that defines the version option for a command.
+///
+public class CommandVersionOptionAttribute : CommandOptionAttribute
+{
+ ///
+ /// Initializes an instance of .
+ ///
+ public CommandVersionOptionAttribute()
+ : base("version")
+ {
+ Description = "Show application version.";
+ }
+}
diff --git a/CliFx/CliApplication.cs b/CliFx/CliApplication.cs
index a67d821..57a4063 100644
--- a/CliFx/CliApplication.cs
+++ b/CliFx/CliApplication.cs
@@ -41,16 +41,6 @@ public class CliApplication(
private bool IsPreviewModeEnabled(CommandInput commandInput) =>
Configuration.IsPreviewModeAllowed && commandInput.IsPreviewDirectiveSpecified;
- private bool ShouldShowHelpText(CommandSchema commandSchema, CommandInput commandInput) =>
- commandSchema.IsHelpOptionAvailable && commandInput.IsHelpOptionSpecified
- ||
- // Show help text also if the fallback default command is executed without any arguments
- commandSchema == FallbackDefaultCommand.Schema
- && !commandInput.HasArguments;
-
- private bool ShouldShowVersionText(CommandSchema commandSchema, CommandInput commandInput) =>
- commandSchema.IsVersionOptionAvailable && commandInput.IsVersionOptionSpecified;
-
private async ValueTask PromptDebuggerAsync()
{
using (console.WithForegroundColor(ConsoleColor.Green))
@@ -119,30 +109,36 @@ public class CliApplication(
commandSchema.GetValues(commandInstance)
);
- // Handle the help option
- if (ShouldShowHelpText(commandSchema, commandInput))
- {
- console.WriteHelpText(helpContext);
- return 0;
- }
-
- // Handle the version option
- if (ShouldShowVersionText(commandSchema, commandInput))
- {
- console.WriteLine(Metadata.Version);
- return 0;
- }
-
// Starting from this point, we may produce exceptions that are meant for the
// end-user of the application (i.e. invalid input, command exception, etc).
// Catch these exceptions here, print them to the console, and don't let them
// propagate further.
try
{
- // Bind and execute the command
+ // Bind the command input to the command instance
_commandBinder.Bind(commandInput, commandSchema, commandInstance);
- await commandInstance.ExecuteAsync(console);
+ // Handle the version option
+ if (commandInstance is ICommandWithVersionOption { IsVersionRequested: true })
+ {
+ console.WriteLine(Metadata.Version);
+ return 0;
+ }
+
+ // Handle the help option
+ if (
+ commandInstance
+ is ICommandWithHelpOption { IsHelpRequested: true }
+ // Fallback default command always shows help, even if the option is not specified
+ or FallbackDefaultCommand
+ )
+ {
+ console.WriteHelpText(helpContext);
+ return 0;
+ }
+
+ // Execute the command
+ await commandInstance.ExecuteAsync(console);
return 0;
}
catch (CliFxException ex)
diff --git a/CliFx/CliApplicationBuilder.cs b/CliFx/CliApplicationBuilder.cs
index 107a84f..9a8dc14 100644
--- a/CliFx/CliApplicationBuilder.cs
+++ b/CliFx/CliApplicationBuilder.cs
@@ -175,7 +175,7 @@ public partial class CliApplicationBuilder
[UnconditionalSuppressMessage(
"SingleFile",
"IL3000:Avoid accessing Assembly file path when publishing as a single file",
- Justification = "The return value of the method is checked to ensure the assembly location is available."
+ Justification = "The file path is checked to ensure the assembly location is available."
)]
private static string GetDefaultExecutableName()
{
diff --git a/CliFx/CliFx.csproj b/CliFx/CliFx.csproj
index 67fbfe4..d584fd7 100644
--- a/CliFx/CliFx.csproj
+++ b/CliFx/CliFx.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;netstandard2.1;net8.0
+ netstandard2.0;netstandard2.1;net7.0;net8.0
true
true
true
diff --git a/CliFx/CommandBinder.cs b/CliFx/CommandBinder.cs
index 56bc4ed..202f123 100644
--- a/CliFx/CommandBinder.cs
+++ b/CliFx/CommandBinder.cs
@@ -16,7 +16,7 @@ internal class CommandBinder(ITypeActivator typeActivator)
{
private readonly IFormatProvider _formatProvider = CultureInfo.InvariantCulture;
- private object? ConvertSingle(IInputSchema inputSchema, string? rawValue, Type targetType)
+ private object? ConvertSingle(InputSchema inputSchema, string? rawValue, Type targetType)
{
// Custom converter
if (inputSchema.Converter is not null)
@@ -103,7 +103,7 @@ internal class CommandBinder(ITypeActivator typeActivator)
}
private object? ConvertMultiple(
- IInputSchema inputSchema,
+ InputSchema inputSchema,
IReadOnlyList rawValues,
Type targetEnumerableType,
Type targetElementType
@@ -137,7 +137,7 @@ internal class CommandBinder(ITypeActivator typeActivator)
);
}
- private object? ConvertMember(IInputSchema inputSchema, IReadOnlyList rawValues)
+ private object? ConvertMember(InputSchema inputSchema, IReadOnlyList rawValues)
{
try
{
@@ -192,7 +192,7 @@ internal class CommandBinder(ITypeActivator typeActivator)
);
}
- private void ValidateMember(IInputSchema inputSchema, object? convertedValue)
+ private void ValidateMember(InputSchema inputSchema, object? convertedValue)
{
var errors = new List();
@@ -218,7 +218,7 @@ internal class CommandBinder(ITypeActivator typeActivator)
}
private void BindMember(
- IInputSchema inputSchema,
+ InputSchema inputSchema,
ICommand commandInstance,
IReadOnlyList rawValues
)
@@ -335,7 +335,7 @@ internal class CommandBinder(ITypeActivator typeActivator)
// Environment variable
else if (environmentVariableInput is not null)
{
- var rawValues = optionSchema.IsScalar
+ var rawValues = optionSchema.IsSequence
? [environmentVariableInput.Value]
: environmentVariableInput.SplitValues();
diff --git a/CliFx/FallbackDefaultCommand.cs b/CliFx/FallbackDefaultCommand.cs
index ef175f1..8dab98c 100644
--- a/CliFx/FallbackDefaultCommand.cs
+++ b/CliFx/FallbackDefaultCommand.cs
@@ -9,8 +9,14 @@ namespace CliFx;
// Fallback command used when the application doesn't have one configured.
// This command is only used as a stub for help text.
[Command]
-internal partial class FallbackDefaultCommand : ICommand
+internal partial class FallbackDefaultCommand : ICommandWithHelpOption, ICommandWithVersionOption
{
+ [CommandHelpOption]
+ public bool IsHelpRequested { get; init; }
+
+ [CommandVersionOption]
+ public bool IsVersionRequested { get; init; }
+
// Never actually executed
[ExcludeFromCodeCoverage]
public ValueTask ExecuteAsync(IConsole console) => default;
diff --git a/CliFx/Formatting/HelpConsoleFormatter.cs b/CliFx/Formatting/HelpConsoleFormatter.cs
index ba4fec1..a66f568 100644
--- a/CliFx/Formatting/HelpConsoleFormatter.cs
+++ b/CliFx/Formatting/HelpConsoleFormatter.cs
@@ -69,7 +69,7 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
{
Write(
ConsoleColor.DarkCyan,
- parameter.Property.IsScalar() ? $"<{parameter.Name}>" : $"<{parameter.Name}...>"
+ parameter.IsSequence ? $"<{parameter.Name}...>" : $"<{parameter.Name}>"
);
Write(' ');
}
@@ -85,7 +85,7 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
);
Write(' ');
- Write(ConsoleColor.White, option.Property.IsScalar() ? "" : "");
+ Write(ConsoleColor.White, option.IsSequence ? "" : "");
Write(' ');
}
@@ -170,8 +170,8 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
}
// Valid values
- var validValues = parameterSchema.Property.GetValidValues();
- if (validValues.Any())
+ var validValues = parameterSchema.Property.TryGetValidValues();
+ if (validValues?.Any() == true)
{
Write(ConsoleColor.White, "Choices: ");
@@ -257,8 +257,8 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
}
// Valid values
- var validValues = optionSchema.Property.GetValidValues();
- if (validValues.Any())
+ var validValues = optionSchema.Property.TryGetValidValues();
+ if (validValues?.Any() == true)
{
Write(ConsoleColor.White, "Choices: ");
@@ -305,7 +305,7 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
}
}
- private void WriteDefaultValue(IMemberSchema schema)
+ private void WriteDefaultValue(InputSchema schema)
{
var defaultValue = context.CommandDefaultValues.GetValueOrDefault(schema);
if (defaultValue is not null)
diff --git a/CliFx/Formatting/HelpContext.cs b/CliFx/Formatting/HelpContext.cs
index c898ec6..9f6b84e 100644
--- a/CliFx/Formatting/HelpContext.cs
+++ b/CliFx/Formatting/HelpContext.cs
@@ -7,7 +7,7 @@ internal class HelpContext(
ApplicationMetadata applicationMetadata,
ApplicationSchema applicationSchema,
CommandSchema commandSchema,
- IReadOnlyDictionary commandDefaultValues
+ IReadOnlyDictionary commandDefaultValues
)
{
public ApplicationMetadata ApplicationMetadata { get; } = applicationMetadata;
@@ -16,6 +16,6 @@ internal class HelpContext(
public CommandSchema CommandSchema { get; } = commandSchema;
- public IReadOnlyDictionary CommandDefaultValues { get; } =
+ public IReadOnlyDictionary CommandDefaultValues { get; } =
commandDefaultValues;
}
diff --git a/CliFx/ICommandWithHelpOption.cs b/CliFx/ICommandWithHelpOption.cs
new file mode 100644
index 0000000..bc24477
--- /dev/null
+++ b/CliFx/ICommandWithHelpOption.cs
@@ -0,0 +1,12 @@
+namespace CliFx;
+
+///
+/// Command definition that includes the help option.
+///
+public interface ICommandWithHelpOption : ICommand
+{
+ ///
+ /// Whether the user requested help for this command (via the `-h|--help` option).
+ ///
+ bool IsHelpRequested { get; }
+}
diff --git a/CliFx/ICommandWithVersionOption.cs b/CliFx/ICommandWithVersionOption.cs
new file mode 100644
index 0000000..3ad23d8
--- /dev/null
+++ b/CliFx/ICommandWithVersionOption.cs
@@ -0,0 +1,12 @@
+namespace CliFx;
+
+///
+/// Command definition that includes the version option.
+///
+public interface ICommandWithVersionOption : ICommand
+{
+ ///
+ /// Whether the user requested the version information (via the `--version` option).
+ ///
+ bool IsVersionRequested { get; }
+}
diff --git a/CliFx/Schema/CommandSchema.cs b/CliFx/Schema/CommandSchema.cs
index 5659737..1715582 100644
--- a/CliFx/Schema/CommandSchema.cs
+++ b/CliFx/Schema/CommandSchema.cs
@@ -16,7 +16,7 @@ public class CommandSchema(
)
{
///
- /// Command's CLR type.
+ /// Underlying CLR type of the command.
///
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
public Type Type { get; } = type;
@@ -26,6 +26,11 @@ public class CommandSchema(
///
public string? Name { get; } = name;
+ ///
+ /// Whether this command is the application's default command.
+ ///
+ public bool IsDefault { get; } = string.IsNullOrWhiteSpace(name);
+
///
/// Command description.
///
@@ -41,19 +46,14 @@ public class CommandSchema(
///
public IReadOnlyList Options { get; } = options;
- ///
- /// Whether this command is the application's default command.
- ///
- public bool IsDefault { get; } = string.IsNullOrWhiteSpace(name);
-
internal bool MatchesName(string? name) =>
!string.IsNullOrWhiteSpace(Name)
? string.Equals(name, Name, StringComparison.OrdinalIgnoreCase)
: string.IsNullOrWhiteSpace(name);
- internal IReadOnlyDictionary GetValues(ICommand instance)
+ internal IReadOnlyDictionary GetValues(ICommand instance)
{
- var result = new Dictionary();
+ var result = new Dictionary();
foreach (var parameterSchema in Parameters)
{
diff --git a/CliFx/Schema/IInputSchema.cs b/CliFx/Schema/IInputSchema.cs
deleted file mode 100644
index 8ca594c..0000000
--- a/CliFx/Schema/IInputSchema.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Collections.Generic;
-using CliFx.Extensibility;
-
-namespace CliFx.Schema;
-
-///
-/// Describes an input of a command, which can be either a parameter or an option.
-///
-public interface IInputSchema
-{
- ///
- /// Describes the binding of this input to a CLR property.
- ///
- PropertyBinding Property { get; }
-
- ///
- /// Optional binding converter for this input.
- ///
- IBindingConverter? Converter { get; }
-
- ///
- /// Optional binding validator(s) for this input.
- ///
- IReadOnlyList Validators { get; }
-}
diff --git a/CliFx/Schema/InputSchema.cs b/CliFx/Schema/InputSchema.cs
new file mode 100644
index 0000000..fe57764
--- /dev/null
+++ b/CliFx/Schema/InputSchema.cs
@@ -0,0 +1,35 @@
+using System.Collections.Generic;
+using CliFx.Extensibility;
+
+namespace CliFx.Schema;
+
+///
+/// Describes an input of a command, which can be either a parameter or an option.
+///
+public abstract class InputSchema(
+ PropertyBinding property,
+ bool isSequence,
+ IBindingConverter? converter,
+ IReadOnlyList validators
+)
+{
+ ///
+ /// CLR property to which this input is bound.
+ ///
+ public PropertyBinding Property { get; } = property;
+
+ ///
+ /// Whether this input can accept more than one value.
+ ///
+ public bool IsSequence { get; } = isSequence;
+
+ ///
+ /// Optional binding converter for this input.
+ ///
+ public IBindingConverter? Converter { get; } = converter;
+
+ ///
+ /// Optional binding validator(s) for this input.
+ ///
+ public IReadOnlyList Validators { get; } = validators;
+}
diff --git a/CliFx/Schema/OptionSchema.cs b/CliFx/Schema/OptionSchema.cs
index 7e93bdd..493760e 100644
--- a/CliFx/Schema/OptionSchema.cs
+++ b/CliFx/Schema/OptionSchema.cs
@@ -6,12 +6,11 @@ using CliFx.Extensibility;
namespace CliFx.Schema;
///
-/// Describes a command's option.
+/// Describes an option input of a command.
///
public class OptionSchema(
PropertyBinding property,
- bool isScalar,
- IReadOnlyList