mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
asd
This commit is contained in:
@@ -6,7 +6,7 @@ namespace CliFx.Attributes;
|
||||
/// Annotates a type that defines a command.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public sealed class CommandAttribute : Attribute
|
||||
public class CommandAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandAttribute" />.
|
||||
|
||||
16
CliFx/Attributes/CommandHelpOptionAttribute.cs
Normal file
16
CliFx/Attributes/CommandHelpOptionAttribute.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace CliFx.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Annotates a property that defines the help option for a command.
|
||||
/// </summary>
|
||||
public class CommandHelpOptionAttribute : CommandOptionAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandHelpOptionAttribute" />.
|
||||
/// </summary>
|
||||
public CommandHelpOptionAttribute()
|
||||
: base("help", 'h')
|
||||
{
|
||||
Description = "Show help for this command.";
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace CliFx.Attributes;
|
||||
/// Annotates a property that defines a command option.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class CommandOptionAttribute : Attribute
|
||||
public class CommandOptionAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandOptionAttribute" />.
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace CliFx.Attributes;
|
||||
/// Annotates a property that defines a command parameter.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class CommandParameterAttribute(int order) : Attribute
|
||||
public class CommandParameterAttribute(int order) : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameter order.
|
||||
|
||||
16
CliFx/Attributes/CommandVersionOptionAttribute.cs
Normal file
16
CliFx/Attributes/CommandVersionOptionAttribute.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace CliFx.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Annotates a property that defines the version option for a command.
|
||||
/// </summary>
|
||||
public class CommandVersionOptionAttribute : CommandOptionAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandVersionOptionAttribute" />.
|
||||
/// </summary>
|
||||
public CommandVersionOptionAttribute()
|
||||
: base("version")
|
||||
{
|
||||
Description = "Show application version.";
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;netstandard2.1;net7.0;net8.0</TargetFrameworks>
|
||||
<IsPackable>true</IsPackable>
|
||||
<IsTrimmable Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0'))">true</IsTrimmable>
|
||||
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>
|
||||
|
||||
@@ -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<string> rawValues,
|
||||
Type targetEnumerableType,
|
||||
Type targetElementType
|
||||
@@ -137,7 +137,7 @@ internal class CommandBinder(ITypeActivator typeActivator)
|
||||
);
|
||||
}
|
||||
|
||||
private object? ConvertMember(IInputSchema inputSchema, IReadOnlyList<string> rawValues)
|
||||
private object? ConvertMember(InputSchema inputSchema, IReadOnlyList<string> 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<BindingValidationError>();
|
||||
|
||||
@@ -218,7 +218,7 @@ internal class CommandBinder(ITypeActivator typeActivator)
|
||||
}
|
||||
|
||||
private void BindMember(
|
||||
IInputSchema inputSchema,
|
||||
InputSchema inputSchema,
|
||||
ICommand commandInstance,
|
||||
IReadOnlyList<string> 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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() ? "<value>" : "<values...>");
|
||||
Write(ConsoleColor.White, option.IsSequence ? "<values...>" : "<value>");
|
||||
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)
|
||||
|
||||
@@ -7,7 +7,7 @@ internal class HelpContext(
|
||||
ApplicationMetadata applicationMetadata,
|
||||
ApplicationSchema applicationSchema,
|
||||
CommandSchema commandSchema,
|
||||
IReadOnlyDictionary<IInputSchema, object?> commandDefaultValues
|
||||
IReadOnlyDictionary<InputSchema, object?> commandDefaultValues
|
||||
)
|
||||
{
|
||||
public ApplicationMetadata ApplicationMetadata { get; } = applicationMetadata;
|
||||
@@ -16,6 +16,6 @@ internal class HelpContext(
|
||||
|
||||
public CommandSchema CommandSchema { get; } = commandSchema;
|
||||
|
||||
public IReadOnlyDictionary<IInputSchema, object?> CommandDefaultValues { get; } =
|
||||
public IReadOnlyDictionary<InputSchema, object?> CommandDefaultValues { get; } =
|
||||
commandDefaultValues;
|
||||
}
|
||||
|
||||
12
CliFx/ICommandWithHelpOption.cs
Normal file
12
CliFx/ICommandWithHelpOption.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace CliFx;
|
||||
|
||||
/// <summary>
|
||||
/// Command definition that includes the help option.
|
||||
/// </summary>
|
||||
public interface ICommandWithHelpOption : ICommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the user requested help for this command (via the `-h|--help` option).
|
||||
/// </summary>
|
||||
bool IsHelpRequested { get; }
|
||||
}
|
||||
12
CliFx/ICommandWithVersionOption.cs
Normal file
12
CliFx/ICommandWithVersionOption.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace CliFx;
|
||||
|
||||
/// <summary>
|
||||
/// Command definition that includes the version option.
|
||||
/// </summary>
|
||||
public interface ICommandWithVersionOption : ICommand
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the user requested the version information (via the `--version` option).
|
||||
/// </summary>
|
||||
bool IsVersionRequested { get; }
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public class CommandSchema(
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
/// Command's CLR type.
|
||||
/// Underlying CLR type of the command.
|
||||
/// </summary>
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
|
||||
public Type Type { get; } = type;
|
||||
@@ -26,6 +26,11 @@ public class CommandSchema(
|
||||
/// </summary>
|
||||
public string? Name { get; } = name;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this command is the application's default command.
|
||||
/// </summary>
|
||||
public bool IsDefault { get; } = string.IsNullOrWhiteSpace(name);
|
||||
|
||||
/// <summary>
|
||||
/// Command description.
|
||||
/// </summary>
|
||||
@@ -41,19 +46,14 @@ public class CommandSchema(
|
||||
/// </summary>
|
||||
public IReadOnlyList<OptionSchema> Options { get; } = options;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this command is the application's default command.
|
||||
/// </summary>
|
||||
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<IInputSchema, object?> GetValues(ICommand instance)
|
||||
internal IReadOnlyDictionary<InputSchema, object?> GetValues(ICommand instance)
|
||||
{
|
||||
var result = new Dictionary<IInputSchema, object?>();
|
||||
var result = new Dictionary<InputSchema, object?>();
|
||||
|
||||
foreach (var parameterSchema in Parameters)
|
||||
{
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using CliFx.Extensibility;
|
||||
|
||||
namespace CliFx.Schema;
|
||||
|
||||
/// <summary>
|
||||
/// Describes an input of a command, which can be either a parameter or an option.
|
||||
/// </summary>
|
||||
public interface IInputSchema
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the binding of this input to a CLR property.
|
||||
/// </summary>
|
||||
PropertyBinding Property { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional binding converter for this input.
|
||||
/// </summary>
|
||||
IBindingConverter? Converter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional binding validator(s) for this input.
|
||||
/// </summary>
|
||||
IReadOnlyList<IBindingValidator> Validators { get; }
|
||||
}
|
||||
35
CliFx/Schema/InputSchema.cs
Normal file
35
CliFx/Schema/InputSchema.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using CliFx.Extensibility;
|
||||
|
||||
namespace CliFx.Schema;
|
||||
|
||||
/// <summary>
|
||||
/// Describes an input of a command, which can be either a parameter or an option.
|
||||
/// </summary>
|
||||
public abstract class InputSchema(
|
||||
PropertyBinding property,
|
||||
bool isSequence,
|
||||
IBindingConverter? converter,
|
||||
IReadOnlyList<IBindingValidator> validators
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
/// CLR property to which this input is bound.
|
||||
/// </summary>
|
||||
public PropertyBinding Property { get; } = property;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this input can accept more than one value.
|
||||
/// </summary>
|
||||
public bool IsSequence { get; } = isSequence;
|
||||
|
||||
/// <summary>
|
||||
/// Optional binding converter for this input.
|
||||
/// </summary>
|
||||
public IBindingConverter? Converter { get; } = converter;
|
||||
|
||||
/// <summary>
|
||||
/// Optional binding validator(s) for this input.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IBindingValidator> Validators { get; } = validators;
|
||||
}
|
||||
@@ -6,12 +6,11 @@ using CliFx.Extensibility;
|
||||
namespace CliFx.Schema;
|
||||
|
||||
/// <summary>
|
||||
/// Describes a command's option.
|
||||
/// Describes an option input of a command.
|
||||
/// </summary>
|
||||
public class OptionSchema(
|
||||
PropertyBinding property,
|
||||
bool isScalar,
|
||||
IReadOnlyList<object?>? validValues,
|
||||
bool isSequence,
|
||||
string? name,
|
||||
char? shortName,
|
||||
string? environmentVariable,
|
||||
@@ -19,17 +18,8 @@ public class OptionSchema(
|
||||
string? description,
|
||||
IBindingConverter? converter,
|
||||
IReadOnlyList<IBindingValidator> validators
|
||||
) : IInputSchema
|
||||
) : InputSchema(property, isSequence, converter, validators)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public PropertyBinding Property { get; } = property;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsScalar { get; } = isScalar;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<object?>? ValidValues { get; } = validValues;
|
||||
|
||||
/// <summary>
|
||||
/// Option name.
|
||||
/// </summary>
|
||||
@@ -55,12 +45,6 @@ public class OptionSchema(
|
||||
/// </summary>
|
||||
public string? Description { get; } = description;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IBindingConverter? Converter { get; } = converter;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<IBindingValidator> Validators { get; } = validators;
|
||||
|
||||
internal bool MatchesName(string? name) =>
|
||||
!string.IsNullOrWhiteSpace(Name)
|
||||
&& string.Equals(Name, name, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
@@ -4,29 +4,19 @@ using CliFx.Extensibility;
|
||||
namespace CliFx.Schema;
|
||||
|
||||
/// <summary>
|
||||
/// Describes a command's parameter.
|
||||
/// Describes a parameter input of a command.
|
||||
/// </summary>
|
||||
public class ParameterSchema(
|
||||
PropertyBinding property,
|
||||
bool isScalar,
|
||||
IReadOnlyList<object?>? validValues,
|
||||
bool isSequence,
|
||||
int order,
|
||||
string name,
|
||||
bool isRequired,
|
||||
string? description,
|
||||
IBindingConverter? converter,
|
||||
IReadOnlyList<IBindingValidator> validators
|
||||
) : IInputSchema
|
||||
) : InputSchema(property, isSequence, converter, validators)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public PropertyBinding Property { get; } = property;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsScalar { get; } = isScalar;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<object?>? ValidValues { get; } = validValues;
|
||||
|
||||
/// <summary>
|
||||
/// Order, in which the parameter is bound from the command-line arguments.
|
||||
/// </summary>
|
||||
@@ -47,12 +37,5 @@ public class ParameterSchema(
|
||||
/// </summary>
|
||||
public string? Description { get; } = description;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IBindingConverter? Converter { get; } = converter;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<IBindingValidator> Validators { get; } = validators;
|
||||
|
||||
internal string GetFormattedIdentifier() =>
|
||||
IsScalar ? $"<{Name}>" : $"<{Name}...>";
|
||||
internal string GetFormattedIdentifier() => IsSequence ? $"<{Name}>" : $"<{Name}...>";
|
||||
}
|
||||
|
||||
@@ -5,18 +5,18 @@ using System.Linq;
|
||||
namespace CliFx.Schema;
|
||||
|
||||
/// <summary>
|
||||
/// Describes a CLR property.
|
||||
/// Represents a binding to a CLR property.
|
||||
/// </summary>
|
||||
public class PropertyBinding(
|
||||
Type propertyType,
|
||||
Type type,
|
||||
Func<object, object?> getValue,
|
||||
Action<object, object?> setValue
|
||||
)
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
/// Underlying property type.
|
||||
/// Underlying CLR type of the property.
|
||||
/// </summary>
|
||||
public Type PropertyType { get; } = propertyType;
|
||||
public Type Type { get; } = type;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value of the property on the specified instance.
|
||||
@@ -27,12 +27,23 @@ public class PropertyBinding(
|
||||
/// Sets the value of the property on the specified instance.
|
||||
/// </summary>
|
||||
public void SetValue(object instance, object? value) => setValue(instance, value);
|
||||
}
|
||||
|
||||
internal static class PropertyBindingExtensions
|
||||
{
|
||||
public static IReadOnlyList<object?>? TryGetValidValues(this PropertyBinding binding) =>
|
||||
binding.PropertyType.IsEnum
|
||||
? binding.PropertyType.GetEnumValuesAsUnderlyingType().Cast<object?>().ToArray()
|
||||
: null;
|
||||
internal IReadOnlyList<object?>? TryGetValidValues()
|
||||
{
|
||||
if (Type.IsEnum)
|
||||
{
|
||||
var values =
|
||||
#if NET7_0_OR_GREATER
|
||||
Type.GetEnumValuesAsUnderlyingType();
|
||||
#else
|
||||
// AOT-compatible APIs are not available here, but it's unlikely
|
||||
// someone will be AOT-compiling a net6.0 or older app anyway.
|
||||
Type.GetEnumValues();
|
||||
#endif
|
||||
|
||||
return values.Cast<object?>().ToArray();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
@@ -8,13 +9,17 @@ namespace CliFx.Utils.Extensions;
|
||||
|
||||
internal static class TypeExtensions
|
||||
{
|
||||
public static bool Implements(this Type type, Type interfaceType) =>
|
||||
type.GetInterfaces().Contains(interfaceType);
|
||||
public static bool Implements(
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type,
|
||||
Type interfaceType
|
||||
) => type.GetInterfaces().Contains(interfaceType);
|
||||
|
||||
public static Type? TryGetNullableUnderlyingType(this Type type) =>
|
||||
Nullable.GetUnderlyingType(type);
|
||||
|
||||
public static Type? TryGetEnumerableUnderlyingType(this Type type)
|
||||
public static Type? TryGetEnumerableUnderlyingType(
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type
|
||||
)
|
||||
{
|
||||
if (type.IsPrimitive)
|
||||
return null;
|
||||
@@ -35,24 +40,20 @@ internal static class TypeExtensions
|
||||
}
|
||||
|
||||
public static MethodInfo? TryGetStaticParseMethod(
|
||||
this Type type,
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type,
|
||||
bool withFormatProvider = false
|
||||
)
|
||||
{
|
||||
var argumentTypes = withFormatProvider
|
||||
? new[] { typeof(string), typeof(IFormatProvider) }
|
||||
: new[] { typeof(string) };
|
||||
|
||||
return type.GetMethod(
|
||||
) =>
|
||||
type.GetMethod(
|
||||
"Parse",
|
||||
BindingFlags.Public | BindingFlags.Static,
|
||||
null,
|
||||
argumentTypes,
|
||||
withFormatProvider ? [typeof(string), typeof(IFormatProvider)] : [typeof(string)],
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public static bool IsToStringOverriden(this Type type)
|
||||
public static bool IsToStringOverriden(
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type
|
||||
)
|
||||
{
|
||||
var toStringMethod = type.GetMethod(nameof(ToString), Type.EmptyTypes);
|
||||
return toStringMethod?.GetBaseDefinition()?.DeclaringType != toStringMethod?.DeclaringType;
|
||||
|
||||
Reference in New Issue
Block a user