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;
|
||||
}
|
||||
|
||||
[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")]
|
||||
private class EnvironmentVariableCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -238,6 +238,34 @@ namespace CliFx.Tests
|
||||
_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]
|
||||
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
|
||||
{
|
||||
private IReadOnlyList<string>? _validValues;
|
||||
public PropertyInfo Property { get; }
|
||||
|
||||
public string? Description { get; }
|
||||
@@ -18,12 +19,52 @@ namespace CliFx.Domain
|
||||
|
||||
public bool IsScalar => TryGetEnumerableArgumentUnderlyingType() == null;
|
||||
|
||||
public IReadOnlyList<string> GetValidValues() => _validValues ??
|
||||
(_validValues = EnumerateValidValues().ToList().AsReadOnly());
|
||||
|
||||
protected CommandArgumentSchema(PropertyInfo property, string? description)
|
||||
{
|
||||
Property = property;
|
||||
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() =>
|
||||
Property.PropertyType != typeof(string)
|
||||
? Property.PropertyType.GetEnumerableUnderlyingType()
|
||||
|
||||
@@ -246,18 +246,25 @@ namespace CliFx.Domain
|
||||
|
||||
RenderColumnIndent();
|
||||
|
||||
// Description
|
||||
if (!string.IsNullOrWhiteSpace(option.Description))
|
||||
{
|
||||
Render(option.Description);
|
||||
Render(" ");
|
||||
}
|
||||
|
||||
var validValues = option.GetValidValues();
|
||||
if (validValues.Any())
|
||||
{
|
||||
Render($"Valid values: {string.Join(", ", validValues)}.");
|
||||
Render(" ");
|
||||
}
|
||||
|
||||
// TODO: Render default value here.
|
||||
|
||||
// Environment variable
|
||||
if (!string.IsNullOrWhiteSpace(option.EnvironmentVariableName))
|
||||
{
|
||||
Render($"(Environment variable: {option.EnvironmentVariableName}).");
|
||||
Render(" ");
|
||||
Render($"(Environment variable: {option.EnvironmentVariableName})");
|
||||
}
|
||||
|
||||
RenderNewLine();
|
||||
|
||||
Reference in New Issue
Block a user