This commit is contained in:
Tyrrrz
2024-08-11 22:34:50 +03:00
parent 651146c97b
commit 2d3c221b48
7 changed files with 98 additions and 82 deletions

View File

@@ -305,7 +305,7 @@ internal class HelpConsoleFormatter(ConsoleWriter consoleWriter, HelpContext con
}
}
private void WriteDefaultValue(InputSchema schema)
private void WriteDefaultValue(CommandInputSchema schema)
{
var defaultValue = context.CommandDefaultValues.GetValueOrDefault(schema);
if (defaultValue is null)

View File

@@ -7,7 +7,7 @@ internal class HelpContext(
ApplicationMetadata applicationMetadata,
ApplicationSchema applicationSchema,
CommandSchema commandSchema,
IReadOnlyDictionary<InputSchema, object?> commandDefaultValues
IReadOnlyDictionary<CommandInputSchema, object?> commandDefaultValues
)
{
public ApplicationMetadata ApplicationMetadata { get; } = applicationMetadata;
@@ -16,6 +16,6 @@ internal class HelpContext(
public CommandSchema CommandSchema { get; } = commandSchema;
public IReadOnlyDictionary<InputSchema, object?> CommandDefaultValues { get; } =
public IReadOnlyDictionary<CommandInputSchema, object?> CommandDefaultValues { get; } =
commandDefaultValues;
}

View File

@@ -12,13 +12,17 @@ namespace CliFx.Schema;
/// <summary>
/// Describes an input of a command.
/// </summary>
public abstract class InputSchema(
public abstract class CommandInputSchema(
PropertyBinding property,
string? description,
IBindingConverter converter,
IReadOnlyList<IBindingValidator> validators
)
{
internal abstract string Kind { get; }
internal abstract string FormattedIdentifier { get; }
internal bool IsSequence { get; } =
property.Type != typeof(string)
&& property.Type.TryGetEnumerableUnderlyingType() is not null;
@@ -43,10 +47,6 @@ public abstract class InputSchema(
/// </summary>
public IReadOnlyList<IBindingValidator> Validators { get; } = validators;
internal abstract string GetKind();
internal abstract string GetFormattedIdentifier();
private void Validate(object? value)
{
var errors = Validators
@@ -58,7 +58,7 @@ public abstract class InputSchema(
{
throw CliFxException.UserError(
$"""
{GetKind()} {GetFormattedIdentifier()} has been provided with an invalid value.
{Kind} {FormattedIdentifier} has been provided with an invalid value.
Error(s):
{errors.Select(e => "- " + e.Message).JoinToString(Environment.NewLine)}
"""
@@ -93,7 +93,7 @@ public abstract class InputSchema(
{
throw CliFxException.UserError(
$"""
{GetKind()} {GetFormattedIdentifier()} expects a single argument, but provided with multiple:
{Kind} {FormattedIdentifier} expects a single argument, but provided with multiple:
{rawInputs.Select(v => '<' + v + '>').JoinToString(" ")}
"""
);
@@ -103,7 +103,7 @@ public abstract class InputSchema(
{
throw CliFxException.UserError(
$"""
{GetKind()} {GetFormattedIdentifier()} cannot be set from the provided argument(s):
{Kind} {FormattedIdentifier} cannot be set from the provided argument(s):
{rawInputs.Select(v => '<' + v + '>').JoinToString(" ")}
Error: {ex.Message}
""",
@@ -111,13 +111,18 @@ public abstract class InputSchema(
);
}
}
/// <inheritdoc />
public override string ToString() => FormattedIdentifier;
}
// 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<
/// <inheritdoc cref="CommandInputSchema" />
/// <remarks>
/// Generic version of the type is used to simplify initialization from source-generated code and
/// to enforce static references to all types used in the binding.
/// The non-generic version is used internally by the framework when operating in a dynamic context.
/// </remarks>
public abstract class CommandInputSchema<
TCommand,
[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
@@ -128,5 +133,5 @@ public abstract class InputSchema<
string? description,
BindingConverter<TProperty> converter,
IReadOnlyList<BindingValidator<TProperty>> validators
) : InputSchema(property, description, converter, validators)
) : CommandInputSchema(property, description, converter, validators)
where TCommand : ICommand;

View File

@@ -9,7 +9,7 @@ namespace CliFx.Schema;
/// <summary>
/// Describes an option input of a command.
/// </summary>
public class OptionSchema(
public class CommandOptionSchema(
PropertyBinding property,
string? name,
char? shortName,
@@ -18,8 +18,38 @@ public class OptionSchema(
string? description,
IBindingConverter converter,
IReadOnlyList<IBindingValidator> validators
) : InputSchema(property,description, converter, validators)
) : CommandInputSchema(property,description, converter, validators)
{
internal override string Kind => "Option";
internal override string FormattedIdentifier
{
get
{
var buffer = new StringBuilder();
// Short name
if (ShortName is not null)
{
buffer.Append('-').Append(ShortName);
}
// Separator
if (!string.IsNullOrWhiteSpace(Name) && ShortName is not null)
{
buffer.Append('|');
}
// Name
if (!string.IsNullOrWhiteSpace(Name))
{
buffer.Append("--").Append(Name);
}
return buffer.ToString();
}
}
/// <summary>
/// Option name.
/// </summary>
@@ -53,40 +83,15 @@ public class OptionSchema(
internal bool MatchesEnvironmentVariable(string environmentVariableName) =>
!string.IsNullOrWhiteSpace(EnvironmentVariable)
&& string.Equals(EnvironmentVariable, environmentVariableName, StringComparison.Ordinal);
internal override string GetKind() => "Option";
internal override string GetFormattedIdentifier()
{
var buffer = new StringBuilder();
// Short name
if (ShortName is not null)
{
buffer.Append('-').Append(ShortName);
}
// Separator
if (!string.IsNullOrWhiteSpace(Name) && ShortName is not null)
{
buffer.Append('|');
}
// Name
if (!string.IsNullOrWhiteSpace(Name))
{
buffer.Append("--").Append(Name);
}
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<
/// <inheritdoc cref="CommandOptionSchema" />
/// <remarks>
/// Generic version of the type is used to simplify initialization from source-generated code and
/// to enforce static references to all types used in the binding.
/// The non-generic version is used internally by the framework when operating in a dynamic context.
/// </remarks>
public class CommandOptionSchema<
TCommand,
[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
@@ -102,7 +107,7 @@ public class OptionSchema<
BindingConverter<TProperty> converter,
IReadOnlyList<BindingValidator<TProperty>> validators
)
: OptionSchema(
: CommandOptionSchema(
property,
name,
shortName,

View File

@@ -7,7 +7,7 @@ namespace CliFx.Schema;
/// <summary>
/// Describes a parameter input of a command.
/// </summary>
public class ParameterSchema(
public class CommandParameterSchema(
PropertyBinding property,
int order,
string name,
@@ -15,8 +15,12 @@ public class ParameterSchema(
string? description,
IBindingConverter converter,
IReadOnlyList<IBindingValidator> validators
) : InputSchema(property, description, converter, validators)
) : CommandInputSchema(property, description, converter, validators)
{
internal override string Kind => "Parameter";
internal override string FormattedIdentifier => IsSequence ? $"<{Name}>" : $"<{Name}...>";
/// <summary>
/// Order, in which the parameter is bound from the command-line arguments.
/// </summary>
@@ -31,17 +35,15 @@ public class ParameterSchema(
/// Whether the parameter is required.
/// </summary>
public bool IsRequired { get; } = isRequired;
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
// 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<
/// <inheritdoc cref="CommandParameterSchema" />
/// <remarks>
/// Generic version of the type is used to simplify initialization from source-generated code and
/// to enforce static references to all types used in the binding.
/// The non-generic version is used internally by the framework when operating in a dynamic context.
/// </remarks>
public class CommandParameterSchema<
TCommand,
[DynamicallyAccessedMembers(
DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicMethods
@@ -55,5 +57,5 @@ public class ParameterSchema<
string? description,
BindingConverter<TProperty> converter,
IReadOnlyList<BindingValidator<TProperty>> validators
) : ParameterSchema(property, order, name, isRequired, description, converter, validators)
) : CommandParameterSchema(property, order, name, isRequired, description, converter, validators)
where TCommand : ICommand;

View File

@@ -9,13 +9,13 @@ using CliFx.Utils.Extensions;
namespace CliFx.Schema;
/// <summary>
/// Describes an individual command, along with its parameter and option inputs.
/// Describes an individual command, along with its inputs.
/// </summary>
public class CommandSchema(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type,
string? name,
string? description,
IReadOnlyList<InputSchema> inputs
IReadOnlyList<CommandInputSchema> inputs
)
{
/// <summary>
@@ -40,29 +40,29 @@ public class CommandSchema(
public string? Description { get; } = description;
/// <summary>
/// Inputs (parameters and options) of the command.
/// Command inputs.
/// </summary>
public IReadOnlyList<InputSchema> Inputs { get; } = inputs;
public IReadOnlyList<CommandInputSchema> Inputs { get; } = inputs;
/// <summary>
/// Parameter inputs of the command.
/// </summary>
public IReadOnlyList<ParameterSchema> Parameters { get; } =
inputs.OfType<ParameterSchema>().ToArray();
public IReadOnlyList<CommandParameterSchema> Parameters { get; } =
inputs.OfType<CommandParameterSchema>().ToArray();
/// <summary>
/// Option inputs of the command.
/// </summary>
public IReadOnlyList<OptionSchema> Options { get; } = inputs.OfType<OptionSchema>().ToArray();
public IReadOnlyList<CommandOptionSchema> Options { get; } = inputs.OfType<CommandOptionSchema>().ToArray();
internal bool MatchesName(string? name) =>
!string.IsNullOrWhiteSpace(Name)
? string.Equals(name, Name, StringComparison.OrdinalIgnoreCase)
: string.IsNullOrWhiteSpace(name);
internal IReadOnlyDictionary<InputSchema, object?> GetValues(ICommand instance)
internal IReadOnlyDictionary<CommandInputSchema, object?> GetValues(ICommand instance)
{
var result = new Dictionary<InputSchema, object?>();
var result = new Dictionary<CommandInputSchema, object?>();
foreach (var parameterSchema in Parameters)
{
@@ -135,7 +135,7 @@ public class CommandSchema(
$"""
Missing required parameter(s):
{remainingRequiredParameterSchemas
.Select(p => p.GetFormattedIdentifier())
.Select(p => p.FormattedIdentifier)
.JoinToString(" ")}
"""
);
@@ -208,7 +208,7 @@ public class CommandSchema(
$"""
Missing required option(s):
{remainingRequiredOptionSchemas
.Select(o => o.GetFormattedIdentifier())
.Select(o => o.FormattedIdentifier)
.JoinToString(", ")}
"""
);
@@ -222,12 +222,14 @@ public class CommandSchema(
}
}
// 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" />
/// <remarks>
/// Generic version of the type is used to simplify initialization from source-generated code and
/// to enforce static references to all types used in the binding.
/// The non-generic version is used internally by the framework when operating in a dynamic context.
/// </remarks>
public class CommandSchema<
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TCommand
>(string? name, string? description, IReadOnlyList<InputSchema> inputs)
>(string? name, string? description, IReadOnlyList<CommandInputSchema> inputs)
: CommandSchema(typeof(TCommand), name, description, inputs)
where TCommand : ICommand;

View File

@@ -55,10 +55,12 @@ public class PropertyBinding(
}
}
// 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" />
/// <remarks>
/// Generic version of the type is used to simplify initialization from source-generated code and
/// to enforce static references to all types used in the binding.
/// The non-generic version is used internally by the framework when operating in a dynamic context.
/// </remarks>
public class PropertyBinding<
TObject,
[DynamicallyAccessedMembers(