mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Show valid values of an enum option in help (#53)
This commit is contained in:
@@ -82,6 +82,20 @@ namespace CliFx.Tests
|
|||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Command("cmd-with-enum-opts")]
|
||||||
|
private class EnumOptionsCommand : ICommand
|
||||||
|
{
|
||||||
|
public enum ValuesEnum { Value1, Value2, Value3 };
|
||||||
|
|
||||||
|
[CommandOption("value", Description = "Enum option.", IsRequired = true)]
|
||||||
|
public ValuesEnum Value { get; set; } = ValuesEnum.Value1;
|
||||||
|
|
||||||
|
[CommandOption("nullable-value", Description = "Nullable enum option.")]
|
||||||
|
public ValuesEnum? NullableValue { get; set; }
|
||||||
|
|
||||||
|
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||||
|
}
|
||||||
|
|
||||||
[Command("cmd-with-env-vars")]
|
[Command("cmd-with-env-vars")]
|
||||||
private class EnvironmentVariableCommand : ICommand
|
private class EnvironmentVariableCommand : ICommand
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -238,6 +238,34 @@ namespace CliFx.Tests
|
|||||||
_output.WriteLine(stdOutData);
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Help_text_shows_usage_format_which_lists_all_valid_values_for_enum_options()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var stdOut = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand(typeof(EnumOptionsCommand))
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await application.RunAsync(new[] { "cmd-with-enum-opts", "--help" });
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
stdOutData.Should().ContainAll(
|
||||||
|
"Usage",
|
||||||
|
"cmd-with-enum-opts", "[options]",
|
||||||
|
"Options",
|
||||||
|
"* --value", "Enum option.", "Valid values: Value1, Value2, Value3.",
|
||||||
|
"--nullable-value", "Nullable enum option.", "Valid values: Value1, Value2, Value3."
|
||||||
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Help_text_lists_environment_variable_names_for_options_that_have_them_defined()
|
public async Task Help_text_lists_environment_variable_names_for_options_that_have_them_defined()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ namespace CliFx.Domain
|
|||||||
{
|
{
|
||||||
internal abstract partial class CommandArgumentSchema
|
internal abstract partial class CommandArgumentSchema
|
||||||
{
|
{
|
||||||
|
private IReadOnlyList<string>? _validValues;
|
||||||
public PropertyInfo Property { get; }
|
public PropertyInfo Property { get; }
|
||||||
|
|
||||||
public string? Description { get; }
|
public string? Description { get; }
|
||||||
@@ -18,12 +19,52 @@ namespace CliFx.Domain
|
|||||||
|
|
||||||
public bool IsScalar => TryGetEnumerableArgumentUnderlyingType() == null;
|
public bool IsScalar => TryGetEnumerableArgumentUnderlyingType() == null;
|
||||||
|
|
||||||
|
public IReadOnlyList<string> GetValidValues() => _validValues ??
|
||||||
|
(_validValues = EnumerateValidValues().ToList().AsReadOnly());
|
||||||
|
|
||||||
protected CommandArgumentSchema(PropertyInfo property, string? description)
|
protected CommandArgumentSchema(PropertyInfo property, string? description)
|
||||||
{
|
{
|
||||||
Property = property;
|
Property = property;
|
||||||
Description = description;
|
Description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<string> EnumerateValidValues()
|
||||||
|
{
|
||||||
|
var propertyType = Property?.PropertyType;
|
||||||
|
|
||||||
|
// Property can actually be null here due to damn it operators
|
||||||
|
// in CommandOptionSchema lines 103 and 106, so we have to check
|
||||||
|
// for now. In such case that it is null, let's end early.
|
||||||
|
if (propertyType is null)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If 'propertyType' is nullable, this will return a non-null value.
|
||||||
|
var underlyingType = propertyType.GetNullableUnderlyingType();
|
||||||
|
|
||||||
|
// If 'propertyType' is nullable, 'underlying' type will be not null.
|
||||||
|
if (underlyingType is object)
|
||||||
|
{
|
||||||
|
// Handle nullable num.
|
||||||
|
if (underlyingType.IsEnum)
|
||||||
|
{
|
||||||
|
// Reasign so we can do the 'foreach' over the enum values
|
||||||
|
// only once at the end of the method.
|
||||||
|
propertyType = underlyingType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle non-nullable enums or nullable enums that were "unwrapped".
|
||||||
|
if (propertyType.IsEnum)
|
||||||
|
{
|
||||||
|
foreach (var value in Enum.GetValues(propertyType))
|
||||||
|
{
|
||||||
|
yield return value.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Type? TryGetEnumerableArgumentUnderlyingType() =>
|
private Type? TryGetEnumerableArgumentUnderlyingType() =>
|
||||||
Property.PropertyType != typeof(string)
|
Property.PropertyType != typeof(string)
|
||||||
? Property.PropertyType.GetEnumerableUnderlyingType()
|
? Property.PropertyType.GetEnumerableUnderlyingType()
|
||||||
|
|||||||
@@ -246,18 +246,25 @@ namespace CliFx.Domain
|
|||||||
|
|
||||||
RenderColumnIndent();
|
RenderColumnIndent();
|
||||||
|
|
||||||
// Description
|
|
||||||
if (!string.IsNullOrWhiteSpace(option.Description))
|
if (!string.IsNullOrWhiteSpace(option.Description))
|
||||||
{
|
{
|
||||||
Render(option.Description);
|
Render(option.Description);
|
||||||
Render(" ");
|
Render(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var validValues = option.GetValidValues();
|
||||||
|
if (validValues.Any())
|
||||||
|
{
|
||||||
|
Render($"Valid values: {string.Join(", ", validValues)}.");
|
||||||
|
Render(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Render default value here.
|
||||||
|
|
||||||
// Environment variable
|
// Environment variable
|
||||||
if (!string.IsNullOrWhiteSpace(option.EnvironmentVariableName))
|
if (!string.IsNullOrWhiteSpace(option.EnvironmentVariableName))
|
||||||
{
|
{
|
||||||
Render($"(Environment variable: {option.EnvironmentVariableName}).");
|
Render($"(Environment variable: {option.EnvironmentVariableName})");
|
||||||
Render(" ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderNewLine();
|
RenderNewLine();
|
||||||
|
|||||||
Reference in New Issue
Block a user