Throw errors on unrecognized input

Closes #38
Closes #24
This commit is contained in:
Alexey Golub
2020-03-16 14:48:48 +02:00
parent f38bd32510
commit c854f5fb8d
3 changed files with 72 additions and 0 deletions

View File

@@ -166,5 +166,17 @@ namespace CliFx.Tests
public ValueTask ExecuteAsync(IConsole console) => default; public ValueTask ExecuteAsync(IConsole console) => default;
} }
[Command]
private class NoParameterCommand : ICommand
{
[CommandOption(nameof(OptionA))]
public string? OptionA { get; set; }
[CommandOption(nameof(OptionB))]
public string? OptionB { get; set; }
public ValueTask ExecuteAsync(IConsole console) => default;
}
} }
} }

View File

@@ -1018,5 +1018,37 @@ namespace CliFx.Tests
// Act & assert // Act & assert
Assert.Throws<CliFxException>(() => schema.InitializeEntryPoint(input)); Assert.Throws<CliFxException>(() => schema.InitializeEntryPoint(input));
} }
[Fact]
public void All_provided_option_arguments_must_be_bound_to_corresponding_properties()
{
// Arrange
var schema = ApplicationSchema.Resolve(new[] {typeof(AllSupportedTypesCommand)});
var input = new CommandLineInputBuilder()
.AddOption("not-a-real-option", "boom")
.AddOption("fake-option", "poof")
.Build();
// Act & assert
Assert.Throws<CliFxException>(() => schema.InitializeEntryPoint(input));
}
[Fact]
public void All_provided_parameter_arguments_must_be_bound_to_corresponding_properties()
{
// Arrange
var schema = ApplicationSchema.Resolve(new[] {typeof(NoParameterCommand)});
var input = new CommandLineInputBuilder()
.AddUnboundArgument("boom")
.AddUnboundArgument("poof")
.AddOption(nameof(NoParameterCommand.OptionA), "foo")
.AddOption(nameof(NoParameterCommand.OptionB), "bar")
.Build();
// Act & assert
Assert.Throws<CliFxException>(() => schema.InitializeEntryPoint(input));
}
} }
} }

View File

@@ -42,6 +42,9 @@ namespace CliFx.Domain
private void InjectParameters(ICommand command, IReadOnlyList<string> parameterInputs) private void InjectParameters(ICommand command, IReadOnlyList<string> parameterInputs)
{ {
// All inputs must be bound
var remainingParameterInputs = parameterInputs.ToList();
// Scalar parameters // Scalar parameters
var scalarParameters = Parameters var scalarParameters = Parameters
.OrderBy(p => p.Order) .OrderBy(p => p.Order)
@@ -57,6 +60,7 @@ namespace CliFx.Domain
: throw new CliFxException($"Missing value for parameter <{scalarParameter.DisplayName}>."); : throw new CliFxException($"Missing value for parameter <{scalarParameter.DisplayName}>.");
scalarParameter.Inject(command, scalarParameterInput); scalarParameter.Inject(command, scalarParameterInput);
remainingParameterInputs.Remove(scalarParameterInput);
} }
// Non-scalar parameter (only one is allowed) // Non-scalar parameter (only one is allowed)
@@ -68,6 +72,16 @@ namespace CliFx.Domain
{ {
var nonScalarParameterInputs = parameterInputs.Skip(scalarParameters.Length).ToArray(); var nonScalarParameterInputs = parameterInputs.Skip(scalarParameters.Length).ToArray();
nonScalarParameter.Inject(command, nonScalarParameterInputs); nonScalarParameter.Inject(command, nonScalarParameterInputs);
remainingParameterInputs.Clear();
}
// Ensure all inputs were bound
if (remainingParameterInputs.Any())
{
throw new CliFxException(new StringBuilder()
.AppendLine("Unrecognized parameters provided:")
.AppendBulletList(remainingParameterInputs)
.ToString());
} }
} }
@@ -76,6 +90,9 @@ namespace CliFx.Domain
IReadOnlyList<CommandOptionInput> optionInputs, IReadOnlyList<CommandOptionInput> optionInputs,
IReadOnlyDictionary<string, string> environmentVariables) IReadOnlyDictionary<string, string> environmentVariables)
{ {
// All inputs must be bound
var remainingOptionInputs = optionInputs.ToList();
// Keep track of required options so that we can raise an error if any of them are not set // Keep track of required options so that we can raise an error if any of them are not set
var unsetRequiredOptions = Options.Where(o => o.IsRequired).ToList(); var unsetRequiredOptions = Options.Where(o => o.IsRequired).ToList();
@@ -103,10 +120,12 @@ namespace CliFx.Domain
if (option != null) if (option != null)
{ {
option.Inject(command, optionInput.Values); option.Inject(command, optionInput.Values);
remainingOptionInputs.Remove(optionInput);
unsetRequiredOptions.Remove(option); unsetRequiredOptions.Remove(option);
} }
} }
// Ensure all required options were set
if (unsetRequiredOptions.Any()) if (unsetRequiredOptions.Any())
{ {
throw new CliFxException(new StringBuilder() throw new CliFxException(new StringBuilder()
@@ -114,6 +133,15 @@ namespace CliFx.Domain
.AppendBulletList(unsetRequiredOptions.Select(o => o.DisplayName)) .AppendBulletList(unsetRequiredOptions.Select(o => o.DisplayName))
.ToString()); .ToString());
} }
// Ensure all inputs were bound
if (remainingOptionInputs.Any())
{
throw new CliFxException(new StringBuilder()
.AppendLine("Unrecognized options provided:")
.AppendBulletList(remainingOptionInputs.Select(o => o.Alias))
.ToString());
}
} }
public ICommand CreateInstance( public ICommand CreateInstance(