Skip to content

Commit 889ac3b

Browse files
authored
Sequences that hit max stop grabbing values (#682)
If a sequence option (one with IEnumerable or similar type) has a Max=N assigned, then once it has hit N values it will stop grabbing values from the command line, and any remaining values will be able to be assigned to other properties with the Value attribute.
1 parent 074d050 commit 889ac3b

File tree

3 files changed

+37
-26
lines changed

3 files changed

+37
-26
lines changed

Diff for: src/CommandLine/Core/TokenPartitioner.cs

+12-12
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ public static Tuple<IEnumerable<Token>, IEnumerable<Token>, IEnumerable<Token>,
9696
case SequenceState.TokenSearch:
9797
case SequenceState.ScalarTokenFound when nameToken == null:
9898
case SequenceState.SequenceTokenFound when nameToken == null:
99-
// if (nameToken == null) Console.WriteLine($" (because there was no nameToken)");
10099
nameToken = null;
101100
nonOptionTokens.Add(token);
102101
state = SequenceState.TokenSearch;
@@ -110,24 +109,25 @@ public static Tuple<IEnumerable<Token>, IEnumerable<Token>, IEnumerable<Token>,
110109

111110
case SequenceState.SequenceTokenFound:
112111
if (sequences.TryGetValue(nameToken, out var sequence)) {
113-
// if (max[nameToken].MatchJust(out int m) && count[nameToken] >= m)
114-
// {
115-
// // This sequence is completed, so this and any further values are non-option values
116-
// nameToken = null;
117-
// nonOptionTokens.Add(token);
118-
// state = SequenceState.TokenSearch;
119-
// }
120-
// else
112+
if (max[nameToken].MatchJust(out int m) && count[nameToken] >= m)
113+
{
114+
// This sequence is completed, so this and any further values are non-option values
115+
nameToken = null;
116+
nonOptionTokens.Add(token);
117+
state = SequenceState.TokenSearch;
118+
}
119+
else
121120
{
122121
sequence.Add(token);
123122
count[nameToken]++;
124123
}
125124
}
126125
else
127126
{
128-
Console.WriteLine("***BUG!!!***");
129-
throw new NullReferenceException($"Sequence for name {nameToken} doesn't exist, and it should");
130-
// sequences[nameToken] = new List<Token>(new[] { token });
127+
// Should never get here, but just in case:
128+
sequences[nameToken] = new List<Token>(new[] { token });
129+
count[nameToken] = 0;
130+
max[nameToken] = Maybe.Nothing<int>();
131131
}
132132
break;
133133
}

Diff for: tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs

+14-13
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public void Parse_string_sequence_with_only_max_constraint(string[] arguments, s
185185
}
186186

187187
[Fact]
188-
public void Breaking_min_constraint_in_string_sequence_gererates_MissingValueOptionError()
188+
public void Breaking_min_constraint_in_string_sequence_generates_MissingValueOptionError()
189189
{
190190
// Fixture setup
191191
var expectedResult = new[] { new MissingValueOptionError(new NameInfo("s", "string-seq")) };
@@ -199,7 +199,7 @@ public void Breaking_min_constraint_in_string_sequence_gererates_MissingValueOpt
199199
}
200200

201201
[Fact]
202-
public void Breaking_min_constraint_in_string_sequence_as_value_gererates_SequenceOutOfRangeError()
202+
public void Breaking_min_constraint_in_string_sequence_as_value_generates_SequenceOutOfRangeError()
203203
{
204204
// Fixture setup
205205
var expectedResult = new[] { new SequenceOutOfRangeError(NameInfo.EmptyName) };
@@ -213,21 +213,22 @@ public void Breaking_min_constraint_in_string_sequence_as_value_gererates_Sequen
213213
}
214214

215215
[Fact]
216-
public void Breaking_max_constraint_in_string_sequence_gererates_SequenceOutOfRangeError()
216+
public void Breaking_max_constraint_in_string_sequence_does_not_generate_SequenceOutOfRangeError()
217217
{
218218
// Fixture setup
219-
var expectedResult = new[] { new SequenceOutOfRangeError(new NameInfo("s", "string-seq")) };
219+
var expectedResult = new[] { "one", "two", "three" };
220220

221221
// Exercize system
222222
var result = InvokeBuild<Options_With_Sequence_And_Only_Max_Constraint>(
223223
new[] { "--string-seq=one", "two", "three", "this-is-too-much" });
224224

225225
// Verify outcome
226-
((NotParsed<Options_With_Sequence_And_Only_Max_Constraint>)result).Errors.Should().BeEquivalentTo(expectedResult);
226+
((Parsed<Options_With_Sequence_And_Only_Max_Constraint>)result).Value.StringSequence.Should().BeEquivalentTo(expectedResult);
227+
// The "this-is-too-much" arg would end up assigned to a Value; since there is no Value, it is silently dropped
227228
}
228229

229230
[Fact]
230-
public void Breaking_max_constraint_in_string_sequence_as_value_gererates_SequenceOutOfRangeError()
231+
public void Breaking_max_constraint_in_string_sequence_as_value_generates_SequenceOutOfRangeError()
231232
{
232233
// Fixture setup
233234
var expectedResult = new[] { new SequenceOutOfRangeError(NameInfo.EmptyName) };
@@ -427,7 +428,7 @@ public void Double_dash_force_subsequent_arguments_as_values()
427428
}
428429

429430
[Fact]
430-
public void Parse_option_from_different_sets_gererates_MutuallyExclusiveSetError()
431+
public void Parse_option_from_different_sets_generates_MutuallyExclusiveSetError()
431432
{
432433
// Fixture setup
433434
var expectedResult = new[]
@@ -480,7 +481,7 @@ public void Two_required_options_at_the_same_set_and_none_are_true()
480481
}
481482

482483
[Fact]
483-
public void Omitting_required_option_gererates_MissingRequiredOptionError()
484+
public void Omitting_required_option_generates_MissingRequiredOptionError()
484485
{
485486
// Fixture setup
486487
var expectedResult = new[] { new MissingRequiredOptionError(new NameInfo("", "str")) };
@@ -494,7 +495,7 @@ public void Omitting_required_option_gererates_MissingRequiredOptionError()
494495
}
495496

496497
[Fact]
497-
public void Wrong_range_in_sequence_gererates_SequenceOutOfRangeError()
498+
public void Wrong_range_in_sequence_generates_SequenceOutOfRangeError()
498499
{
499500
// Fixture setup
500501
var expectedResult = new[] { new SequenceOutOfRangeError(new NameInfo("i", "")) };
@@ -508,7 +509,7 @@ public void Wrong_range_in_sequence_gererates_SequenceOutOfRangeError()
508509
}
509510

510511
[Fact]
511-
public void Parse_unknown_long_option_gererates_UnknownOptionError()
512+
public void Parse_unknown_long_option_generates_UnknownOptionError()
512513
{
513514
// Fixture setup
514515
var expectedResult = new[] { new UnknownOptionError("xyz") };
@@ -522,7 +523,7 @@ public void Parse_unknown_long_option_gererates_UnknownOptionError()
522523
}
523524

524525
[Fact]
525-
public void Parse_unknown_short_option_gererates_UnknownOptionError()
526+
public void Parse_unknown_short_option_generates_UnknownOptionError()
526527
{
527528
// Fixture setup
528529
var expectedResult = new[] { new UnknownOptionError("z") };
@@ -536,7 +537,7 @@ public void Parse_unknown_short_option_gererates_UnknownOptionError()
536537
}
537538

538539
[Fact]
539-
public void Parse_unknown_short_option_in_option_group_gererates_UnknownOptionError()
540+
public void Parse_unknown_short_option_in_option_group_generates_UnknownOptionError()
540541
{
541542
// Fixture setup
542543
var expectedResult = new[] { new UnknownOptionError("z") };
@@ -596,7 +597,7 @@ public void Parse_utf8_string_correctly(string[] arguments, string expected)
596597
}
597598

598599
[Fact]
599-
public void Breaking_equal_min_max_constraint_in_string_sequence_as_value_gererates_SequenceOutOfRangeError()
600+
public void Breaking_equal_min_max_constraint_in_string_sequence_as_value_generates_SequenceOutOfRangeError()
600601
{
601602
// Fixture setup
602603
var expectedResult = new[] { new SequenceOutOfRangeError(NameInfo.EmptyName) };

Diff for: tests/CommandLine.Tests/Unit/Core/SequenceTests.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public void Partition_sequence_multi_instance()
133133
[Fact]
134134
public void Partition_sequence_multi_instance_with_max()
135135
{
136-
var expected = new[]
136+
var incorrect = new[]
137137
{
138138
Token.Name("seq"),
139139
Token.Value("seqval0"),
@@ -144,6 +144,14 @@ public void Partition_sequence_multi_instance_with_max()
144144
Token.Value("seqval5"),
145145
};
146146

147+
var expected = new[]
148+
{
149+
Token.Name("seq"),
150+
Token.Value("seqval0"),
151+
Token.Value("seqval1"),
152+
Token.Value("seqval2"),
153+
};
154+
147155
var tokens = TokenPartitioner.PartitionTokensByType(
148156
new[]
149157
{
@@ -159,6 +167,8 @@ public void Partition_sequence_multi_instance_with_max()
159167
: Maybe.Nothing<TypeDescriptor>());
160168
var result = tokens.Item3; // Switch, Scalar, *Sequence*, NonOption
161169

170+
// Max of 3 will apply to the total values, so there should only be 3 values, not 6
171+
Assert.NotEqual(incorrect, result);
162172
Assert.Equal(expected, result);
163173
}
164174
}

0 commit comments

Comments
 (0)