mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
asd
This commit is contained in:
@@ -97,7 +97,7 @@ public class CliApplication(
|
|||||||
var commandInstance =
|
var commandInstance =
|
||||||
commandSchema == FallbackDefaultCommand.Schema
|
commandSchema == FallbackDefaultCommand.Schema
|
||||||
? new FallbackDefaultCommand() // bypass the activator
|
? new FallbackDefaultCommand() // bypass the activator
|
||||||
: typeActivator.CreateInstance<IBindableCommand>(commandSchema.Type);
|
: typeActivator.CreateInstance<ICommand>(commandSchema.Type);
|
||||||
|
|
||||||
// Assemble the help context
|
// Assemble the help context
|
||||||
var helpContext = new HelpContext(
|
var helpContext = new HelpContext(
|
||||||
@@ -113,8 +113,8 @@ public class CliApplication(
|
|||||||
// propagate further.
|
// propagate further.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Bind the command input to the command instance
|
// Activate the command instance with the provided input
|
||||||
commandInstance.Bind(commandInput);
|
commandSchema.Activate(commandInput, commandInstance);
|
||||||
|
|
||||||
// Handle the version option
|
// Handle the version option
|
||||||
if (commandInstance is ICommandWithVersionOption { IsVersionRequested: true })
|
if (commandInstance is ICommandWithVersionOption { IsVersionRequested: true })
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CliFx.Attributes;
|
using CliFx.Attributes;
|
||||||
using CliFx.Infrastructure;
|
using CliFx.Infrastructure;
|
||||||
using CliFx.Input;
|
|
||||||
using CliFx.Schema;
|
using CliFx.Schema;
|
||||||
|
|
||||||
namespace CliFx;
|
namespace CliFx;
|
||||||
@@ -11,8 +10,7 @@ namespace CliFx;
|
|||||||
// This command is only used as a stub for help text.
|
// This command is only used as a stub for help text.
|
||||||
[Command]
|
[Command]
|
||||||
internal partial class FallbackDefaultCommand
|
internal partial class FallbackDefaultCommand
|
||||||
: IBindableCommand,
|
: ICommandWithHelpOption,
|
||||||
ICommandWithHelpOption,
|
|
||||||
ICommandWithVersionOption
|
ICommandWithVersionOption
|
||||||
{
|
{
|
||||||
[CommandHelpOption]
|
[CommandHelpOption]
|
||||||
@@ -21,11 +19,6 @@ internal partial class FallbackDefaultCommand
|
|||||||
[CommandVersionOption]
|
[CommandVersionOption]
|
||||||
public bool IsVersionRequested { get; init; }
|
public bool IsVersionRequested { get; init; }
|
||||||
|
|
||||||
public void Bind(CommandInput input)
|
|
||||||
{
|
|
||||||
throw new System.NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Never actually executed
|
// Never actually executed
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||||
@@ -33,6 +26,5 @@ internal partial class FallbackDefaultCommand
|
|||||||
|
|
||||||
internal partial class FallbackDefaultCommand
|
internal partial class FallbackDefaultCommand
|
||||||
{
|
{
|
||||||
public static CommandSchema Schema { get; } =
|
public static CommandSchema Schema { get; } = new CommandSchema<FallbackDefaultCommand>(null, null, []);
|
||||||
new(typeof(FallbackDefaultCommand), null, null, [], []);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
using CliFx.Input;
|
|
||||||
|
|
||||||
namespace CliFx;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Command whose inputs can be bound from command-line arguments.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This interface is required to facilitate binding of command inputs (parameters and options)
|
|
||||||
/// to their corresponding CLR properties.
|
|
||||||
/// You should not need to implement this interface directly, as it will be automatically
|
|
||||||
/// implemented by the framework.
|
|
||||||
/// </remarks>
|
|
||||||
public interface IBindableCommand : ICommand
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Binds the command input to the current instance.
|
|
||||||
/// </summary>
|
|
||||||
void Bind(CommandInput input);
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using CliFx.Exceptions;
|
||||||
|
using CliFx.Input;
|
||||||
|
using CliFx.Utils.Extensions;
|
||||||
|
|
||||||
namespace CliFx.Schema;
|
namespace CliFx.Schema;
|
||||||
|
|
||||||
@@ -63,18 +66,160 @@ public class CommandSchema(
|
|||||||
|
|
||||||
foreach (var parameterSchema in Parameters)
|
foreach (var parameterSchema in Parameters)
|
||||||
{
|
{
|
||||||
var value = parameterSchema.Property.Get(instance);
|
var value = parameterSchema.Property.GetValue(instance);
|
||||||
result[parameterSchema] = value;
|
result[parameterSchema] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var optionSchema in Options)
|
foreach (var optionSchema in Options)
|
||||||
{
|
{
|
||||||
var value = optionSchema.Property.Get(instance);
|
var value = optionSchema.Property.GetValue(instance);
|
||||||
result[optionSchema] = value;
|
result[optionSchema] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ActivateParameters(CommandInput input, ICommand instance)
|
||||||
|
{
|
||||||
|
// Ensure there are no unexpected parameters and that all parameters are provided
|
||||||
|
var remainingParameterInputs = input.Parameters.ToList();
|
||||||
|
var remainingRequiredParameterSchemas = Parameters.Where(p => p.IsRequired).ToList();
|
||||||
|
|
||||||
|
var position = 0;
|
||||||
|
|
||||||
|
foreach (var parameterSchema in Parameters.OrderBy(p => p.Order))
|
||||||
|
{
|
||||||
|
// Break when there are no remaining inputs
|
||||||
|
if (position >= input.Parameters.Count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Non-sequence: take one input at the current position
|
||||||
|
if (!parameterSchema.IsSequence)
|
||||||
|
{
|
||||||
|
var parameterInput = input.Parameters[position];
|
||||||
|
parameterSchema.Activate(instance, [parameterInput.Value]);
|
||||||
|
|
||||||
|
position++;
|
||||||
|
remainingParameterInputs.Remove(parameterInput);
|
||||||
|
}
|
||||||
|
// Sequence: take all remaining inputs starting from the current position
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var parameterInputs = input.Parameters.Skip(position).ToArray();
|
||||||
|
|
||||||
|
parameterSchema.Activate(
|
||||||
|
instance,
|
||||||
|
parameterInputs.Select(p => p.Value).ToArray()
|
||||||
|
);
|
||||||
|
|
||||||
|
position += parameterInputs.Length;
|
||||||
|
remainingParameterInputs.RemoveRange(parameterInputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
remainingRequiredParameterSchemas.Remove(parameterSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingParameterInputs.Any())
|
||||||
|
{
|
||||||
|
throw CliFxException.UserError(
|
||||||
|
$"""
|
||||||
|
Unexpected parameter(s):
|
||||||
|
{remainingParameterInputs.Select(p => p.GetFormattedIdentifier()).JoinToString(" ")}
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingRequiredParameterSchemas.Any())
|
||||||
|
{
|
||||||
|
throw CliFxException.UserError(
|
||||||
|
$"""
|
||||||
|
Missing required parameter(s):
|
||||||
|
{remainingRequiredParameterSchemas
|
||||||
|
.Select(p => p.GetFormattedIdentifier())
|
||||||
|
.JoinToString(" ")}
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ActivateOptions(CommandInput input, ICommand instance)
|
||||||
|
{
|
||||||
|
// Ensure there are no unrecognized options and that all required options are set
|
||||||
|
var remainingOptionInputs = input.Options.ToList();
|
||||||
|
var remainingRequiredOptionSchemas = Options.Where(o => o.IsRequired)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var optionSchema in Options)
|
||||||
|
{
|
||||||
|
var optionInputs = input
|
||||||
|
.Options.Where(o => optionSchema.MatchesIdentifier(o.Identifier))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var environmentVariableInput = input.EnvironmentVariables.FirstOrDefault(e =>
|
||||||
|
optionSchema.MatchesEnvironmentVariable(e.Name)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Direct input
|
||||||
|
if (optionInputs.Any())
|
||||||
|
{
|
||||||
|
var rawValues = optionInputs.SelectMany(o => o.Values).ToArray();
|
||||||
|
|
||||||
|
optionSchema.Activate(instance, rawValues);
|
||||||
|
|
||||||
|
// Required options need at least one value to be set
|
||||||
|
if (rawValues.Any())
|
||||||
|
remainingRequiredOptionSchemas.Remove(optionSchema);
|
||||||
|
}
|
||||||
|
// Environment variable
|
||||||
|
else if (environmentVariableInput is not null)
|
||||||
|
{
|
||||||
|
var rawValues = !optionSchema.IsSequence
|
||||||
|
? [environmentVariableInput.Value]
|
||||||
|
: environmentVariableInput.SplitValues();
|
||||||
|
|
||||||
|
optionSchema.Activate(instance, rawValues);
|
||||||
|
|
||||||
|
// Required options need at least one value to be set
|
||||||
|
if (rawValues.Any())
|
||||||
|
remainingRequiredOptionSchemas.Remove(optionSchema);
|
||||||
|
}
|
||||||
|
// No input, skip
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
remainingOptionInputs.RemoveRange(optionInputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingOptionInputs.Any())
|
||||||
|
{
|
||||||
|
throw CliFxException.UserError(
|
||||||
|
$"""
|
||||||
|
Unrecognized option(s):
|
||||||
|
{remainingOptionInputs.Select(o => o.GetFormattedIdentifier()).JoinToString(", ")}
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingRequiredOptionSchemas.Any())
|
||||||
|
{
|
||||||
|
throw CliFxException.UserError(
|
||||||
|
$"""
|
||||||
|
Missing required option(s):
|
||||||
|
{remainingRequiredOptionSchemas
|
||||||
|
.Select(o => o.GetFormattedIdentifier())
|
||||||
|
.JoinToString(", ")}
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Activate(CommandInput input, ICommand instance)
|
||||||
|
{
|
||||||
|
ActivateParameters(input, instance);
|
||||||
|
ActivateOptions(input, instance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic version of the type is used to simplify initialization from the source-generated code
|
// Generic version of the type is used to simplify initialization from the source-generated code
|
||||||
|
|||||||
@@ -28,32 +28,31 @@ public abstract class InputSchema(
|
|||||||
public PropertyBinding Property { get; } = property;
|
public PropertyBinding Property { get; } = property;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Optional binding converter for this input.
|
/// Binding converter used for this input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IBindingConverter Converter { get; } = converter;
|
public IBindingConverter Converter { get; } = converter;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Optional binding validator(s) for this input.
|
/// Binding validator(s) used for this input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyList<IBindingValidator> Validators { get; } = validators;
|
public IReadOnlyList<IBindingValidator> Validators { get; } = validators;
|
||||||
|
|
||||||
internal void Validate(object? value)
|
internal abstract string GetKind();
|
||||||
|
|
||||||
|
internal abstract string GetFormattedIdentifier();
|
||||||
|
|
||||||
|
private void Validate(object? value)
|
||||||
{
|
{
|
||||||
var errors = new List<BindingValidationError>();
|
var errors = Validators
|
||||||
|
.Select(validator => validator.Validate(value))
|
||||||
foreach (var validator in validators)
|
.OfType<BindingValidationError>()
|
||||||
{
|
.ToArray();
|
||||||
var error = validator.Validate(value);
|
|
||||||
|
|
||||||
if (error is not null)
|
|
||||||
errors.Add(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errors.Any())
|
if (errors.Any())
|
||||||
{
|
{
|
||||||
throw CliFxException.UserError(
|
throw CliFxException.UserError(
|
||||||
$"""
|
$"""
|
||||||
{memberSchema.GetKind()} {memberSchema.GetFormattedIdentifier()} has been provided with an invalid value.
|
{GetKind()} {GetFormattedIdentifier()} has been provided with an invalid value.
|
||||||
Error(s):
|
Error(s):
|
||||||
{errors.Select(e => "- " + e.Message).JoinToString(Environment.NewLine)}
|
{errors.Select(e => "- " + e.Message).JoinToString(Environment.NewLine)}
|
||||||
"""
|
"""
|
||||||
@@ -61,34 +60,48 @@ public abstract class InputSchema(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Set(ICommand command, IReadOnlyList<string?> rawInputs)
|
internal void Activate(ICommand instance, IReadOnlyList<string?> rawInputs)
|
||||||
{
|
{
|
||||||
var formatProvider = CultureInfo.InvariantCulture;
|
var formatProvider = CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
// Multiple values expected, single or multiple values provided
|
try
|
||||||
if (IsSequence)
|
|
||||||
{
|
{
|
||||||
var value = rawInputs.Select(v => Converter.Convert(v, formatProvider)).ToArray();
|
// Multiple values expected, single or multiple values provided
|
||||||
Validate(value);
|
if (IsSequence)
|
||||||
|
{
|
||||||
|
var value = rawInputs.Select(v => Converter.Convert(v, formatProvider)).ToArray();
|
||||||
|
Validate(value);
|
||||||
|
|
||||||
Property.Set(command, value);
|
Property.SetValue(instance, value);
|
||||||
}
|
}
|
||||||
// Single value expected, single value provided
|
// Single value expected, single value provided
|
||||||
else if (rawInputs.Count <= 1)
|
else if (rawInputs.Count <= 1)
|
||||||
{
|
{
|
||||||
var value = Converter.Convert(rawInputs.SingleOrDefault(), formatProvider);
|
var value = Converter.Convert(rawInputs.SingleOrDefault(), formatProvider);
|
||||||
Validate(value);
|
Validate(value);
|
||||||
|
|
||||||
Property.Set(command, value);
|
Property.SetValue(instance, value);
|
||||||
|
}
|
||||||
|
// Single value expected, multiple values provided
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw CliFxException.UserError(
|
||||||
|
$"""
|
||||||
|
{GetKind()} {GetFormattedIdentifier()} expects a single argument, but provided with multiple:
|
||||||
|
{rawInputs.Select(v => '<' + v + '>').JoinToString(" ")}
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Single value expected, multiple values provided
|
catch (Exception ex) when (ex is not CliFxException) // don't wrap CliFxException
|
||||||
else
|
|
||||||
{
|
{
|
||||||
throw CliFxException.UserError(
|
throw CliFxException.UserError(
|
||||||
$"""
|
$"""
|
||||||
{memberSchema.GetKind()} {memberSchema.GetFormattedIdentifier()} expects a single argument, but provided with multiple:
|
{GetKind()} {GetFormattedIdentifier()} cannot be set from the provided argument(s):
|
||||||
{rawInputs.Select(v => '<' + v + '>').JoinToString(" ")}
|
{rawInputs.Select(v => '<' + v + '>').JoinToString(" ")}
|
||||||
"""
|
Error: {ex.Message}
|
||||||
|
""",
|
||||||
|
ex
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,9 @@ public class OptionSchema(
|
|||||||
!string.IsNullOrWhiteSpace(EnvironmentVariable)
|
!string.IsNullOrWhiteSpace(EnvironmentVariable)
|
||||||
&& string.Equals(EnvironmentVariable, environmentVariableName, StringComparison.Ordinal);
|
&& string.Equals(EnvironmentVariable, environmentVariableName, StringComparison.Ordinal);
|
||||||
|
|
||||||
internal string GetFormattedIdentifier()
|
internal override string GetKind() => "Option";
|
||||||
|
|
||||||
|
internal override string GetFormattedIdentifier()
|
||||||
{
|
{
|
||||||
var buffer = new StringBuilder();
|
var buffer = new StringBuilder();
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ public class ParameterSchema(
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Description { get; } = description;
|
public string? Description { get; } = description;
|
||||||
|
|
||||||
internal string GetFormattedIdentifier() => IsSequence ? $"<{Name}>" : $"<{Name}...>";
|
internal override string GetKind() => "Parameter";
|
||||||
|
|
||||||
|
internal override string GetFormattedIdentifier() => IsSequence ? $"<{Name}>" : $"<{Name}...>";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic version of the type is used to simplify initialization from the source-generated code
|
// Generic version of the type is used to simplify initialization from the source-generated code
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ public class PropertyBinding(
|
|||||||
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
|
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
|
||||||
)]
|
)]
|
||||||
Type type,
|
Type type,
|
||||||
Func<object, object?> get,
|
Func<object, object?> getValue,
|
||||||
Action<object, object?> set
|
Action<object, object?> setValue
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -28,12 +28,12 @@ public class PropertyBinding(
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the current value of the property on the specified instance.
|
/// Gets the current value of the property on the specified instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object? Get(object instance) => get(instance);
|
public object? GetValue(object instance) => getValue(instance);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the current value of the property on the specified instance.
|
/// Sets the current value of the property on the specified instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Set(object instance, object? value) => set(instance, value);
|
public void SetValue(object instance, object? value) => setValue(instance, value);
|
||||||
|
|
||||||
internal IReadOnlyList<object?>? TryGetValidValues()
|
internal IReadOnlyList<object?>? TryGetValidValues()
|
||||||
{
|
{
|
||||||
@@ -65,9 +65,9 @@ public class PropertyBinding<
|
|||||||
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
|
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
|
||||||
)]
|
)]
|
||||||
TProperty
|
TProperty
|
||||||
>(Func<TObject, TProperty?> get, Action<TObject, TProperty?> set)
|
>(Func<TObject, TProperty?> getValue, Action<TObject, TProperty?> setValue)
|
||||||
: PropertyBinding(
|
: PropertyBinding(
|
||||||
typeof(TProperty),
|
typeof(TProperty),
|
||||||
o => get((TObject)o),
|
o => getValue((TObject)o),
|
||||||
(o, v) => set((TObject)o, (TProperty?)v)
|
(o, v) => setValue((TObject)o, (TProperty?)v)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@@ -14,16 +13,6 @@ internal static class CollectionExtensions
|
|||||||
yield return (o, i++);
|
yield return (o, i++);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source)
|
|
||||||
where T : class
|
|
||||||
{
|
|
||||||
foreach (var i in source)
|
|
||||||
{
|
|
||||||
if (i is not null)
|
|
||||||
yield return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<string> WhereNotNullOrWhiteSpace(this IEnumerable<string?> source)
|
public static IEnumerable<string> WhereNotNullOrWhiteSpace(this IEnumerable<string?> source)
|
||||||
{
|
{
|
||||||
foreach (var i in source)
|
foreach (var i in source)
|
||||||
@@ -47,14 +36,4 @@ internal static class CollectionExtensions
|
|||||||
dictionary
|
dictionary
|
||||||
.Cast<DictionaryEntry>()
|
.Cast<DictionaryEntry>()
|
||||||
.ToDictionary(entry => (TKey)entry.Key, entry => (TValue)entry.Value!, comparer);
|
.ToDictionary(entry => (TKey)entry.Key, entry => (TValue)entry.Value!, comparer);
|
||||||
|
|
||||||
public static Array ToNonGenericArray<T>(this IEnumerable<T> source, Type elementType)
|
|
||||||
{
|
|
||||||
var sourceAsCollection = source as ICollection ?? source.ToArray();
|
|
||||||
|
|
||||||
var array = Array.CreateInstance(elementType, sourceAsCollection.Count);
|
|
||||||
sourceAsCollection.CopyTo(array, 0);
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,20 +3,11 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace CliFx.Utils.Extensions;
|
namespace CliFx.Utils.Extensions;
|
||||||
|
|
||||||
internal static class TypeExtensions
|
internal static class TypeExtensions
|
||||||
{
|
{
|
||||||
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(
|
public static Type? TryGetEnumerableUnderlyingType(
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] this Type type
|
||||||
)
|
)
|
||||||
@@ -39,23 +30,11 @@ internal static class TypeExtensions
|
|||||||
.MaxBy(t => t != typeof(object));
|
.MaxBy(t => t != typeof(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodInfo? TryGetStaticParseMethod(
|
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type,
|
|
||||||
bool withFormatProvider = false
|
|
||||||
) =>
|
|
||||||
type.GetMethod(
|
|
||||||
"Parse",
|
|
||||||
BindingFlags.Public | BindingFlags.Static,
|
|
||||||
null,
|
|
||||||
withFormatProvider ? [typeof(string), typeof(IFormatProvider)] : [typeof(string)],
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
public static bool IsToStringOverriden(
|
public static bool IsToStringOverriden(
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var toStringMethod = type.GetMethod(nameof(ToString), Type.EmptyTypes);
|
var toStringMethod = type.GetMethod(nameof(ToString), Type.EmptyTypes);
|
||||||
return toStringMethod?.GetBaseDefinition()?.DeclaringType != toStringMethod?.DeclaringType;
|
return toStringMethod?.GetBaseDefinition().DeclaringType != toStringMethod?.DeclaringType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user