Skip to content

Commit

Permalink
Throw errors on unrecognized input
Browse files Browse the repository at this point in the history
Closes #38
Closes #24
  • Loading branch information
Tyrrrz committed Mar 16, 2020
1 parent f38bd32 commit c854f5f
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 0 deletions.
12 changes: 12 additions & 0 deletions CliFx.Tests/ArgumentBindingSpecs.Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,5 +166,17 @@ private class UnsupportedEnumerablePropertyTypeCommand : ICommand

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;
}
}
}
32 changes: 32 additions & 0 deletions CliFx.Tests/ArgumentBindingSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1018,5 +1018,37 @@ public void Property_must_have_a_type_that_implements_IEnumerable_in_order_to_be
// Act & assert
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));
}
}
}
28 changes: 28 additions & 0 deletions CliFx/Domain/CommandSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public CommandSchema(

private void InjectParameters(ICommand command, IReadOnlyList<string> parameterInputs)
{
// All inputs must be bound
var remainingParameterInputs = parameterInputs.ToList();

// Scalar parameters
var scalarParameters = Parameters
.OrderBy(p => p.Order)
Expand All @@ -57,6 +60,7 @@ private void InjectParameters(ICommand command, IReadOnlyList<string> parameterI
: throw new CliFxException($"Missing value for parameter <{scalarParameter.DisplayName}>.");

scalarParameter.Inject(command, scalarParameterInput);
remainingParameterInputs.Remove(scalarParameterInput);
}

// Non-scalar parameter (only one is allowed)
Expand All @@ -68,6 +72,16 @@ private void InjectParameters(ICommand command, IReadOnlyList<string> parameterI
{
var nonScalarParameterInputs = parameterInputs.Skip(scalarParameters.Length).ToArray();
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());
}
}

Expand All @@ -76,6 +90,9 @@ private void InjectOptions(
IReadOnlyList<CommandOptionInput> optionInputs,
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
var unsetRequiredOptions = Options.Where(o => o.IsRequired).ToList();

Expand Down Expand Up @@ -103,17 +120,28 @@ private void InjectOptions(
if (option != null)
{
option.Inject(command, optionInput.Values);
remainingOptionInputs.Remove(optionInput);
unsetRequiredOptions.Remove(option);
}
}

// Ensure all required options were set
if (unsetRequiredOptions.Any())
{
throw new CliFxException(new StringBuilder()
.AppendLine("Missing values for some of the required options:")
.AppendBulletList(unsetRequiredOptions.Select(o => o.DisplayName))
.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(
Expand Down

0 comments on commit c854f5f

Please sign in to comment.