Skip to content

Commit d5f3c3b

Browse files
authored
Remove Argument.AllowedValues, use Validator to implement the logic (#1959)
* delegate Option.AcceptOnlyFromAmong to Argument.AcceptOnlyFromAmong * use Validator to implement AllowedValues
1 parent 9eae53a commit d5f3c3b

File tree

6 files changed

+53
-46
lines changed

6 files changed

+53
-46
lines changed

src/System.CommandLine.Tests/ParsingValidationTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,34 @@ public void When_FromAmong_is_used_for_multiple_arguments_and_invalid_input_is_p
119119
.Be(LocalizationResources.Instance.UnrecognizedArgument("not-key1", new[] { "key1", "key2" }));
120120
}
121121

122+
[Fact]
123+
public void When_FromAmong_is_used_multiple_times_only_the_most_recently_provided_values_are_taken_into_account()
124+
{
125+
Argument<string> argument = new("key");
126+
argument.AcceptOnlyFromAmong("key1");
127+
128+
var command = new Command("set")
129+
{
130+
argument
131+
};
132+
133+
var result = command.Parse("set key2");
134+
135+
result.Errors
136+
.Should()
137+
.ContainSingle()
138+
.Which
139+
.Message
140+
.Should()
141+
.Be(LocalizationResources.Instance.UnrecognizedArgument("key2", new[] { "key1" }));
142+
143+
argument.AcceptOnlyFromAmong("key2");
144+
145+
result = command.Parse("set key2");
146+
147+
result.Errors.Should().BeEmpty();
148+
}
149+
122150
[Fact]
123151
public void When_FromAmong_is_used_for_multiple_arguments_and_invalid_input_is_provided_for_the_second_one_then_the_error_is_informative()
124152
{

src/System.CommandLine/Argument.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ protected Argument(string? name = null, string? description = null)
3737
Description = description;
3838
}
3939

40-
internal HashSet<string>? AllowedValues { get; private set; }
41-
4240
/// <summary>
4341
/// Gets or sets the arity of the argument.
4442
/// </summary>
@@ -127,16 +125,6 @@ private protected override string DefaultName
127125

128126
internal virtual bool HasCustomParser => false;
129127

130-
internal void AddAllowedValues(IReadOnlyList<string> values)
131-
{
132-
if (AllowedValues is null)
133-
{
134-
AllowedValues = new HashSet<string>();
135-
}
136-
137-
AllowedValues.UnionWith(values);
138-
}
139-
140128
/// <inheritdoc />
141129
public override IEnumerable<CompletionItem> GetCompletions(CompletionContext context)
142130
{

src/System.CommandLine/Argument{T}.cs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4-
using System.Collections.Generic;
54
using System.CommandLine.Binding;
6-
using System.CommandLine.Completions;
75
using System.CommandLine.Parsing;
86
using System.IO;
97

@@ -179,12 +177,31 @@ public void SetDefaultValueFactory(Func<ArgumentResult, T> defaultValueFactory)
179177
/// <returns>The configured argument.</returns>
180178
public Argument<T> AcceptOnlyFromAmong(params string[] values)
181179
{
182-
AllowedValues?.Clear();
183-
AddAllowedValues(values);
184-
CompletionSources.Clear();
185-
CompletionSources.Add(values);
180+
if (values is not null && values.Length > 0)
181+
{
182+
Validators.Clear();
183+
Validators.Add(UnrecognizedArgumentError);
184+
CompletionSources.Clear();
185+
CompletionSources.Add(values);
186+
}
186187

187188
return this;
189+
190+
void UnrecognizedArgumentError(ArgumentResult argumentResult)
191+
{
192+
for (var i = 0; i < argumentResult.Tokens.Count; i++)
193+
{
194+
var token = argumentResult.Tokens[i];
195+
196+
if (token.Symbol is null || token.Symbol == this)
197+
{
198+
if (Array.IndexOf(values, token.Value) < 0)
199+
{
200+
argumentResult.ErrorMessage = argumentResult.LocalizationResources.UnrecognizedArgument(token.Value, values);
201+
}
202+
}
203+
}
204+
}
188205
}
189206

190207
/// <summary>

src/System.CommandLine/Parsing/ArgumentResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public void OnlyTake(int numberOfTokens)
8383

8484
if (!string.IsNullOrWhiteSpace(ErrorMessage))
8585
{
86-
return new ParseError(ErrorMessage!, this);
86+
return new ParseError(ErrorMessage!, Parent is OptionResult option ? option : this);
8787
}
8888
}
8989

src/System.CommandLine/Parsing/ParseResultVisitor.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -506,9 +506,7 @@ private void ValidateAndConvertArgumentResult(ArgumentResult argumentResult)
506506
{
507507
var argument = argumentResult.Argument;
508508

509-
var parseError =
510-
argumentResult.Parent?.UnrecognizedArgumentError(argument) ??
511-
argumentResult.CustomError(argument);
509+
var parseError = argumentResult.CustomError(argument);
512510

513511
if (parseError is { })
514512
{

src/System.CommandLine/Parsing/SymbolResult.cs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -185,29 +185,5 @@ internal ArgumentResult GetOrCreateDefaultArgumentResult(Argument argument) =>
185185

186186
/// <inheritdoc/>
187187
public override string ToString() => $"{GetType().Name}: {this.Token()} {string.Join(" ", Tokens.Select(t => t.Value))}";
188-
189-
internal ParseError? UnrecognizedArgumentError(Argument argument)
190-
{
191-
if (argument.AllowedValues?.Count > 0 &&
192-
Tokens.Count > 0)
193-
{
194-
for (var i = 0; i < Tokens.Count; i++)
195-
{
196-
var token = Tokens[i];
197-
198-
if (token.Symbol is null || token.Symbol == argument)
199-
{
200-
if (!argument.AllowedValues.Contains(token.Value))
201-
{
202-
return new ParseError(
203-
LocalizationResources.UnrecognizedArgument(token.Value, argument.AllowedValues),
204-
this);
205-
}
206-
}
207-
}
208-
}
209-
210-
return null;
211-
}
212188
}
213189
}

0 commit comments

Comments
 (0)