Allow mixed naming when setting an option to multiple values

This commit is contained in:
Alexey Golub
2020-03-16 19:47:51 +02:00
parent c854f5fb8d
commit b812bd1423
3 changed files with 44 additions and 7 deletions

View File

@@ -122,6 +122,15 @@ namespace CliFx.Tests
public ValueTask ExecuteAsync(IConsole console) => default; public ValueTask ExecuteAsync(IConsole console) => default;
} }
[Command]
private class ArrayOptionCommand : ICommand
{
[CommandOption("option", 'o')]
public IReadOnlyList<string>? Option { get; set; }
public ValueTask ExecuteAsync(IConsole console) => default;
}
[Command] [Command]
private class RequiredOptionCommand : ICommand private class RequiredOptionCommand : ICommand
{ {

View File

@@ -910,6 +910,28 @@ namespace CliFx.Tests
}); });
} }
[Fact]
public void Property_annotated_as_an_option_can_be_bound_from_multiple_values_even_if_the_inputs_use_mixed_naming()
{
// Arrange
var schema = ApplicationSchema.Resolve(new[] {typeof(ArrayOptionCommand)});
var input = new CommandLineInputBuilder()
.AddOption("option", "foo")
.AddOption("o", "bar")
.AddOption("option", "baz")
.Build();
// Act
var command = schema.InitializeEntryPoint(input);
// Assert
command.Should().BeEquivalentTo(new ArrayOptionCommand
{
Option = new[] {"foo", "bar", "baz"}
});
}
[Fact] [Fact]
public void Property_annotated_as_a_required_option_must_always_be_bound_to_some_value() public void Property_annotated_as_a_required_option_must_always_be_bound_to_some_value()
{ {

View File

@@ -93,7 +93,7 @@ namespace CliFx.Domain
// All inputs must be bound // All inputs must be bound
var remainingOptionInputs = optionInputs.ToList(); var remainingOptionInputs = optionInputs.ToList();
// Keep track of required options so that we can raise an error if any of them are not set // All required options must be set
var unsetRequiredOptions = Options.Where(o => o.IsRequired).ToList(); var unsetRequiredOptions = Options.Where(o => o.IsRequired).ToList();
// Environment variables // Environment variables
@@ -112,15 +112,21 @@ namespace CliFx.Domain
} }
} }
// TODO: refactor this part? I wrote this while sick
// Direct input // Direct input
foreach (var optionInput in optionInputs) foreach (var option in Options)
{ {
var option = Options.FirstOrDefault(o => o.MatchesNameOrShortName(optionInput.Alias)); var inputs = optionInputs
.Where(i => option.MatchesNameOrShortName(i.Alias))
.ToArray();
if (option != null) if (inputs.Any())
{ {
option.Inject(command, optionInput.Values); option.Inject(command, inputs.SelectMany(i => i.Values).ToArray());
remainingOptionInputs.Remove(optionInput);
foreach (var input in inputs)
remainingOptionInputs.Remove(input);
unsetRequiredOptions.Remove(option); unsetRequiredOptions.Remove(option);
} }
} }
@@ -139,7 +145,7 @@ namespace CliFx.Domain
{ {
throw new CliFxException(new StringBuilder() throw new CliFxException(new StringBuilder()
.AppendLine("Unrecognized options provided:") .AppendLine("Unrecognized options provided:")
.AppendBulletList(remainingOptionInputs.Select(o => o.Alias)) .AppendBulletList(remainingOptionInputs.Select(o => o.Alias).Distinct())
.ToString()); .ToString());
} }
} }