mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
asd
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace CliFx.Tests.Dummy;
|
namespace CliFx.Tests.Dummy;
|
||||||
@@ -13,7 +12,7 @@ public static class Program
|
|||||||
public static string FilePath { get; } =
|
public static string FilePath { get; } =
|
||||||
Path.ChangeExtension(
|
Path.ChangeExtension(
|
||||||
Assembly.GetExecutingAssembly().Location,
|
Assembly.GetExecutingAssembly().Location,
|
||||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "exe" : null
|
OperatingSystem.IsWindows() ? "exe" : null
|
||||||
);
|
);
|
||||||
|
|
||||||
public static async Task Main()
|
public static async Task Main()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using CliFx.Extensibility;
|
using CliFx.Extensibility;
|
||||||
|
|
||||||
namespace CliFx.Attributes;
|
namespace CliFx.Attributes;
|
||||||
@@ -15,8 +16,9 @@ public class CommandParameterAttribute(int order) : Attribute
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// All parameters in a command must have unique order.
|
/// All parameters in a command must have unique order.
|
||||||
/// Parameter whose type is a non-scalar (e.g. array), must always be the last in order.
|
/// Parameter whose type is a sequence (e.g. Array, <see cref="List{T}" />; except <see cref="string" />),
|
||||||
/// Only one non-scalar parameter is allowed in a command.
|
/// must always be the last parameter based on order.
|
||||||
|
/// Only one sequential parameter is allowed in a command.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public int Order { get; } = order;
|
public int Order { get; } = order;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace CliFx.Extensibility;
|
using System;
|
||||||
|
|
||||||
|
namespace CliFx.Extensibility;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base type for custom converters.
|
/// Base type for custom converters.
|
||||||
@@ -8,7 +10,8 @@ public abstract class BindingConverter<T> : IBindingConverter
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses the value from a raw command-line argument.
|
/// Parses the value from a raw command-line argument.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract T? Convert(string? rawValue);
|
public abstract T? Convert(string? rawValue, IFormatProvider? formatProvider);
|
||||||
|
|
||||||
object? IBindingConverter.Convert(string? rawValue) => Convert(rawValue);
|
object? IBindingConverter.Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||||
|
Convert(rawValue, formatProvider);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace CliFx.Extensibility;
|
using System;
|
||||||
|
|
||||||
|
namespace CliFx.Extensibility;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converter for binding inputs to properties of type <see cref="bool" />.
|
/// Converter for binding inputs to properties of type <see cref="bool" />.
|
||||||
@@ -6,5 +8,6 @@
|
|||||||
public class BoolBindingConverter : BindingConverter<bool>
|
public class BoolBindingConverter : BindingConverter<bool>
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool Convert(string? rawValue) => string.IsNullOrWhiteSpace(rawValue) || bool.Parse(rawValue);
|
public override bool Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||||
|
string.IsNullOrWhiteSpace(rawValue) || bool.Parse(rawValue);
|
||||||
}
|
}
|
||||||
@@ -5,9 +5,10 @@ namespace CliFx.Extensibility;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converter for binding inputs to properties that implement <see cref="IConvertible" />.
|
/// Converter for binding inputs to properties that implement <see cref="IConvertible" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ConvertibleBindingConverter<T>(IFormatProvider formatProvider) : BindingConverter<T> where T: IConvertible
|
public class ConvertibleBindingConverter<T> : BindingConverter<T>
|
||||||
|
where T : IConvertible
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override T? Convert(string? rawValue) =>
|
public override T? Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||||
(T?)System.Convert.ChangeType(rawValue, typeof(T), formatProvider);
|
(T?)System.Convert.ChangeType(rawValue, typeof(T), formatProvider);
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,9 @@ namespace CliFx.Extensibility;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converter for binding inputs to properties of type <see cref="DateTimeOffset" />.
|
/// Converter for binding inputs to properties of type <see cref="DateTimeOffset" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DateTimeOffsetBindingConverter(IFormatProvider formatProvider) : BindingConverter<DateTimeOffset>
|
public class DateTimeOffsetBindingConverter : BindingConverter<DateTimeOffset>
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override DateTimeOffset Convert(string? rawValue) => DateTimeOffset.Parse(rawValue!, formatProvider);
|
public override DateTimeOffset Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||||
|
DateTimeOffset.Parse(rawValue!, formatProvider);
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,16 @@ namespace CliFx.Extensibility;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converter for binding inputs to properties using a custom delegate.
|
/// Converter for binding inputs to properties using a custom delegate.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DelegateBindingConverter<T>(Func<string?, T> convert) : BindingConverter<T>
|
public class DelegateBindingConverter<T>(Func<string?, IFormatProvider?, T> convert)
|
||||||
|
: BindingConverter<T>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes an instance of <see cref="DelegateBindingConverter{T}" />
|
||||||
|
/// </summary>
|
||||||
|
public DelegateBindingConverter(Func<string?, T> convert)
|
||||||
|
: this((rawValue, _) => convert(rawValue)) { }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override T? Convert(string? rawValue) => convert(rawValue);
|
public override T Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||||
|
convert(rawValue, formatProvider);
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,10 @@ namespace CliFx.Extensibility;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converter for binding inputs to properties of type <see cref="Enum" />.
|
/// Converter for binding inputs to properties of type <see cref="Enum" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class EnumBindingConverter<T> : BindingConverter<T> where T : struct, Enum
|
public class EnumBindingConverter<T> : BindingConverter<T>
|
||||||
|
where T : struct, Enum
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override T Convert(string? rawValue) => (T)Enum.Parse(typeof(T), rawValue!, true);
|
public override T Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||||
|
(T)Enum.Parse(typeof(T), rawValue!, true);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace CliFx.Extensibility;
|
using System;
|
||||||
|
|
||||||
|
namespace CliFx.Extensibility;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a custom conversion for binding command-line arguments to command inputs.
|
/// Defines a custom conversion for binding command-line arguments to command inputs.
|
||||||
@@ -11,5 +13,5 @@ public interface IBindingConverter
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses the value from a raw command-line argument.
|
/// Parses the value from a raw command-line argument.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
object? Convert(string? rawValue);
|
object? Convert(string? rawValue, IFormatProvider? formatProvider);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace CliFx.Extensibility;
|
using System;
|
||||||
|
|
||||||
|
namespace CliFx.Extensibility;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converter for binding inputs to properties without any conversion.
|
/// Converter for binding inputs to properties without any conversion.
|
||||||
@@ -6,5 +8,5 @@
|
|||||||
public class NoopBindingConverter : IBindingConverter
|
public class NoopBindingConverter : IBindingConverter
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public object? Convert(string? rawValue) => rawValue;
|
public object? Convert(string? rawValue, IFormatProvider? formatProvider) => rawValue;
|
||||||
}
|
}
|
||||||
@@ -5,11 +5,12 @@ namespace CliFx.Extensibility;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converter for binding inputs to properties of type <see cref="Nullable{T}" />.
|
/// Converter for binding inputs to properties of type <see cref="Nullable{T}" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class NullableBindingConverter<T>(BindingConverter<T> innerConverter) : BindingConverter<T?> where T : struct
|
public class NullableBindingConverter<T>(BindingConverter<T> innerConverter) : BindingConverter<T?>
|
||||||
|
where T : struct
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override T? Convert(string? rawValue) =>
|
public override T? Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||||
!string.IsNullOrWhiteSpace(rawValue)
|
!string.IsNullOrWhiteSpace(rawValue)
|
||||||
? innerConverter.Convert(rawValue)
|
? innerConverter.Convert(rawValue, formatProvider)
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
@@ -5,9 +5,9 @@ namespace CliFx.Extensibility;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converter for binding inputs to properties of type <see cref="TimeSpan" />.
|
/// Converter for binding inputs to properties of type <see cref="TimeSpan" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TimeSpanBindingConverter(IFormatProvider formatProvider) : BindingConverter<TimeSpan>
|
public class TimeSpanBindingConverter : BindingConverter<TimeSpan>
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override TimeSpan Convert(string? rawValue) =>
|
public override TimeSpan Convert(string? rawValue, IFormatProvider? formatProvider) =>
|
||||||
TimeSpan.Parse(rawValue!, formatProvider);
|
TimeSpan.Parse(rawValue!, formatProvider);
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,10 @@ namespace CliFx;
|
|||||||
// Fallback command used when the application doesn't have one configured.
|
// Fallback command used when the application doesn't have one configured.
|
||||||
// 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 : IBindableCommand, ICommandWithHelpOption, ICommandWithVersionOption
|
internal partial class FallbackDefaultCommand
|
||||||
|
: IBindableCommand,
|
||||||
|
ICommandWithHelpOption,
|
||||||
|
ICommandWithVersionOption
|
||||||
{
|
{
|
||||||
[CommandHelpOption]
|
[CommandHelpOption]
|
||||||
public bool IsHelpRequested { get; init; }
|
public bool IsHelpRequested { get; init; }
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace CliFx.Schema;
|
namespace CliFx.Schema;
|
||||||
|
|
||||||
@@ -11,8 +12,7 @@ public class CommandSchema(
|
|||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type,
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type,
|
||||||
string? name,
|
string? name,
|
||||||
string? description,
|
string? description,
|
||||||
IReadOnlyList<ParameterSchema> parameters,
|
IReadOnlyList<InputSchema> inputs
|
||||||
IReadOnlyList<OptionSchema> options
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -36,15 +36,21 @@ public class CommandSchema(
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Description { get; } = description;
|
public string? Description { get; } = description;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inputs (parameters and options) of the command.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<InputSchema> Inputs { get; } = inputs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parameter inputs of the command.
|
/// Parameter inputs of the command.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyList<ParameterSchema> Parameters { get; } = parameters;
|
public IReadOnlyList<ParameterSchema> Parameters { get; } =
|
||||||
|
inputs.OfType<ParameterSchema>().ToArray();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Option inputs of the command.
|
/// Option inputs of the command.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyList<OptionSchema> Options { get; } = options;
|
public IReadOnlyList<OptionSchema> Options { get; } = inputs.OfType<OptionSchema>().ToArray();
|
||||||
|
|
||||||
internal bool MatchesName(string? name) =>
|
internal bool MatchesName(string? name) =>
|
||||||
!string.IsNullOrWhiteSpace(Name)
|
!string.IsNullOrWhiteSpace(Name)
|
||||||
@@ -57,16 +63,26 @@ public class CommandSchema(
|
|||||||
|
|
||||||
foreach (var parameterSchema in Parameters)
|
foreach (var parameterSchema in Parameters)
|
||||||
{
|
{
|
||||||
var value = parameterSchema.Property.GetValue(instance);
|
var value = parameterSchema.Property.Get(instance);
|
||||||
result[parameterSchema] = value;
|
result[parameterSchema] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var optionSchema in Options)
|
foreach (var optionSchema in Options)
|
||||||
{
|
{
|
||||||
var value = optionSchema.Property.GetValue(instance);
|
var value = optionSchema.Property.Get(instance);
|
||||||
result[optionSchema] = value;
|
result[optionSchema] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generic version of the type is used to simplify initialization from the source-generated code
|
||||||
|
// and to enforce static references to all the types used in the binding.
|
||||||
|
// The non-generic version is used internally by the framework when operating in a dynamic context.
|
||||||
|
/// <inheritdoc cref="CommandSchema" />
|
||||||
|
public class CommandSchema<
|
||||||
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TCommand
|
||||||
|
>(string? name, string? description, IReadOnlyList<InputSchema> inputs)
|
||||||
|
: CommandSchema(typeof(TCommand), name, description, inputs)
|
||||||
|
where TCommand : ICommand;
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using CliFx.Exceptions;
|
||||||
using CliFx.Extensibility;
|
using CliFx.Extensibility;
|
||||||
|
using CliFx.Utils.Extensions;
|
||||||
|
|
||||||
namespace CliFx.Schema;
|
namespace CliFx.Schema;
|
||||||
|
|
||||||
@@ -8,28 +14,99 @@ namespace CliFx.Schema;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class InputSchema(
|
public abstract class InputSchema(
|
||||||
PropertyBinding property,
|
PropertyBinding property,
|
||||||
bool isSequence,
|
IBindingConverter converter,
|
||||||
IBindingConverter? converter,
|
|
||||||
IReadOnlyList<IBindingValidator> validators
|
IReadOnlyList<IBindingValidator> validators
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
internal bool IsSequence { get; } =
|
||||||
|
property.Type != typeof(string)
|
||||||
|
&& property.Type.TryGetEnumerableUnderlyingType() is not null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// CLR property to which this input is bound.
|
/// CLR property to which this input is bound.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PropertyBinding Property { get; } = property;
|
public PropertyBinding Property { get; } = property;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the input can accept more than one value.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsSequence { get; } = isSequence;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Optional binding converter for this input.
|
/// Optional binding converter for this input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IBindingConverter? Converter { get; } = converter;
|
public IBindingConverter Converter { get; } = converter;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Optional binding validator(s) for this input.
|
/// Optional binding validator(s) for this input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyList<IBindingValidator> Validators { get; } = validators;
|
public IReadOnlyList<IBindingValidator> Validators { get; } = validators;
|
||||||
|
|
||||||
|
internal void Validate(object? value)
|
||||||
|
{
|
||||||
|
var errors = new List<BindingValidationError>();
|
||||||
|
|
||||||
|
foreach (var validator in validators)
|
||||||
|
{
|
||||||
|
var error = validator.Validate(value);
|
||||||
|
|
||||||
|
if (error is not null)
|
||||||
|
errors.Add(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (errors.Any())
|
||||||
|
{
|
||||||
|
throw CliFxException.UserError(
|
||||||
|
$"""
|
||||||
|
{memberSchema.GetKind()} {memberSchema.GetFormattedIdentifier()} has been provided with an invalid value.
|
||||||
|
Error(s):
|
||||||
|
{errors.Select(e => "- " + e.Message).JoinToString(Environment.NewLine)}
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Set(ICommand command, IReadOnlyList<string?> rawInputs)
|
||||||
|
{
|
||||||
|
var formatProvider = CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
|
// Multiple values expected, single or multiple values provided
|
||||||
|
if (IsSequence)
|
||||||
|
{
|
||||||
|
var value = rawInputs.Select(v => Converter.Convert(v, formatProvider)).ToArray();
|
||||||
|
Validate(value);
|
||||||
|
|
||||||
|
Property.Set(command, value);
|
||||||
|
}
|
||||||
|
// Single value expected, single value provided
|
||||||
|
else if (rawInputs.Count <= 1)
|
||||||
|
{
|
||||||
|
var value = Converter.Convert(rawInputs.SingleOrDefault(), formatProvider);
|
||||||
|
Validate(value);
|
||||||
|
|
||||||
|
Property.Set(command, value);
|
||||||
|
}
|
||||||
|
// Single value expected, multiple values provided
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw CliFxException.UserError(
|
||||||
|
$"""
|
||||||
|
{memberSchema.GetKind()} {memberSchema.GetFormattedIdentifier()} expects a single argument, but provided with multiple:
|
||||||
|
{rawInputs.Select(v => '<' + v + '>').JoinToString(" ")}
|
||||||
|
"""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic version of the type is used to simplify initialization from the source-generated code
|
||||||
|
// and to enforce static references to all the types used in the binding.
|
||||||
|
// The non-generic version is used internally by the framework when operating in a dynamic context.
|
||||||
|
/// <inheritdoc cref="InputSchema" />
|
||||||
|
public abstract class InputSchema<
|
||||||
|
TCommand,
|
||||||
|
[DynamicallyAccessedMembers(
|
||||||
|
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
|
||||||
|
)]
|
||||||
|
TProperty
|
||||||
|
>(
|
||||||
|
PropertyBinding<TCommand, TProperty> property,
|
||||||
|
BindingConverter<TProperty> converter,
|
||||||
|
IReadOnlyList<BindingValidator<TProperty>> validators
|
||||||
|
) : InputSchema(property, converter, validators)
|
||||||
|
where TCommand : ICommand;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using CliFx.Extensibility;
|
using CliFx.Extensibility;
|
||||||
|
|
||||||
@@ -10,15 +11,14 @@ namespace CliFx.Schema;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class OptionSchema(
|
public class OptionSchema(
|
||||||
PropertyBinding property,
|
PropertyBinding property,
|
||||||
bool isSequence,
|
|
||||||
string? name,
|
string? name,
|
||||||
char? shortName,
|
char? shortName,
|
||||||
string? environmentVariable,
|
string? environmentVariable,
|
||||||
bool isRequired,
|
bool isRequired,
|
||||||
string? description,
|
string? description,
|
||||||
IBindingConverter? converter,
|
IBindingConverter converter,
|
||||||
IReadOnlyList<IBindingValidator> validators
|
IReadOnlyList<IBindingValidator> validators
|
||||||
) : InputSchema(property, isSequence, converter, validators)
|
) : InputSchema(property, converter, validators)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Option name.
|
/// Option name.
|
||||||
@@ -84,3 +84,35 @@ public class OptionSchema(
|
|||||||
return buffer.ToString();
|
return buffer.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generic version of the type is used to simplify initialization from the source-generated code
|
||||||
|
// and to enforce static references to all the types used in the binding.
|
||||||
|
// The non-generic version is used internally by the framework when operating in a dynamic context.
|
||||||
|
/// <inheritdoc cref="OptionSchema" />
|
||||||
|
public class OptionSchema<
|
||||||
|
TCommand,
|
||||||
|
[DynamicallyAccessedMembers(
|
||||||
|
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
|
||||||
|
)]
|
||||||
|
TProperty
|
||||||
|
>(
|
||||||
|
PropertyBinding<TCommand, TProperty> property,
|
||||||
|
string? name,
|
||||||
|
char? shortName,
|
||||||
|
string? environmentVariable,
|
||||||
|
bool isRequired,
|
||||||
|
string? description,
|
||||||
|
BindingConverter<TProperty> converter,
|
||||||
|
IReadOnlyList<BindingValidator<TProperty>> validators
|
||||||
|
)
|
||||||
|
: OptionSchema(
|
||||||
|
property,
|
||||||
|
name,
|
||||||
|
shortName,
|
||||||
|
environmentVariable,
|
||||||
|
isRequired,
|
||||||
|
description,
|
||||||
|
converter,
|
||||||
|
validators
|
||||||
|
)
|
||||||
|
where TCommand : ICommand;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using CliFx.Extensibility;
|
using CliFx.Extensibility;
|
||||||
|
|
||||||
namespace CliFx.Schema;
|
namespace CliFx.Schema;
|
||||||
@@ -8,14 +9,13 @@ namespace CliFx.Schema;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ParameterSchema(
|
public class ParameterSchema(
|
||||||
PropertyBinding property,
|
PropertyBinding property,
|
||||||
bool isSequence,
|
|
||||||
int order,
|
int order,
|
||||||
string name,
|
string name,
|
||||||
bool isRequired,
|
bool isRequired,
|
||||||
string? description,
|
string? description,
|
||||||
IBindingConverter? converter,
|
IBindingConverter converter,
|
||||||
IReadOnlyList<IBindingValidator> validators
|
IReadOnlyList<IBindingValidator> validators
|
||||||
) : InputSchema(property, isSequence, converter, validators)
|
) : InputSchema(property, converter, validators)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Order, in which the parameter is bound from the command-line arguments.
|
/// Order, in which the parameter is bound from the command-line arguments.
|
||||||
@@ -39,3 +39,24 @@ public class ParameterSchema(
|
|||||||
|
|
||||||
internal string GetFormattedIdentifier() => IsSequence ? $"<{Name}>" : $"<{Name}...>";
|
internal string GetFormattedIdentifier() => IsSequence ? $"<{Name}>" : $"<{Name}...>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generic version of the type is used to simplify initialization from the source-generated code
|
||||||
|
// and to enforce static references to all the types used in the binding.
|
||||||
|
// The non-generic version is used internally by the framework when operating in a dynamic context.
|
||||||
|
/// <inheritdoc cref="ParameterSchema" />
|
||||||
|
public class ParameterSchema<
|
||||||
|
TCommand,
|
||||||
|
[DynamicallyAccessedMembers(
|
||||||
|
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
|
||||||
|
)]
|
||||||
|
TProperty
|
||||||
|
>(
|
||||||
|
PropertyBinding<TCommand, TProperty> property,
|
||||||
|
int order,
|
||||||
|
string name,
|
||||||
|
bool isRequired,
|
||||||
|
string? description,
|
||||||
|
BindingConverter<TProperty> converter,
|
||||||
|
IReadOnlyList<BindingValidator<TProperty>> validators
|
||||||
|
) : ParameterSchema(property, order, name, isRequired, description, converter, validators)
|
||||||
|
where TCommand : ICommand;
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ public class PropertyBinding(
|
|||||||
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
|
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
|
||||||
)]
|
)]
|
||||||
Type type,
|
Type type,
|
||||||
Func<object, object?> getValue,
|
Func<object, object?> get,
|
||||||
Action<object, object?> setValue
|
Action<object, object?> set
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
/// <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? GetValue(object instance) => getValue(instance);
|
public object? Get(object instance) => get(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 SetValue(object instance, object? value) => setValue(instance, value);
|
public void Set(object instance, object? value) => set(instance, value);
|
||||||
|
|
||||||
internal IReadOnlyList<object?>? TryGetValidValues()
|
internal IReadOnlyList<object?>? TryGetValidValues()
|
||||||
{
|
{
|
||||||
@@ -54,3 +54,20 @@ public class PropertyBinding(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generic version of the type is used to simplify initialization from the source-generated code
|
||||||
|
// and to enforce static references to all the types used in the binding.
|
||||||
|
// The non-generic version is used internally by the framework when operating in a dynamic context.
|
||||||
|
/// <inheritdoc cref="PropertyBinding" />
|
||||||
|
public class PropertyBinding<
|
||||||
|
TObject,
|
||||||
|
[DynamicallyAccessedMembers(
|
||||||
|
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
|
||||||
|
)]
|
||||||
|
TProperty
|
||||||
|
>(Func<TObject, TProperty?> get, Action<TObject, TProperty?> set)
|
||||||
|
: PropertyBinding(
|
||||||
|
typeof(TProperty),
|
||||||
|
o => get((TObject)o),
|
||||||
|
(o, v) => set((TObject)o, (TProperty?)v)
|
||||||
|
);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ internal static class TypeExtensions
|
|||||||
return type.GetGenericArguments().FirstOrDefault();
|
return type.GetGenericArguments().FirstOrDefault();
|
||||||
|
|
||||||
return type.GetInterfaces()
|
return type.GetInterfaces()
|
||||||
.Select(TryGetEnumerableUnderlyingType)
|
.Select(t => TryGetEnumerableUnderlyingType(t))
|
||||||
.Where(t => t is not null)
|
.Where(t => t is not null)
|
||||||
// Every IEnumerable<T> implements IEnumerable (which is essentially IEnumerable<object>),
|
// Every IEnumerable<T> implements IEnumerable (which is essentially IEnumerable<object>),
|
||||||
// so we try to get a more specific underlying type. Still, if the type only implements
|
// so we try to get a more specific underlying type. Still, if the type only implements
|
||||||
|
|||||||
Reference in New Issue
Block a user