Skip to content

Commit 85cf9d3

Browse files
committed
Add FlagCounter flag to OptionAttribute
This will let us parse -vvv and turn that into "Verbose=3".
1 parent 78171b0 commit 85cf9d3

10 files changed

+98
-75
lines changed

src/CommandLine/Core/NameLookup.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace CommandLine.Core
1010
enum NameLookupResult
1111
{
1212
NoOptionFound,
13-
BooleanOptionFound,
13+
FlagOptionFound,
1414
OtherOptionFound
1515
}
1616

@@ -20,8 +20,8 @@ public static NameLookupResult Contains(string name, IEnumerable<OptionSpecifica
2020
{
2121
var option = specifications.FirstOrDefault(a => name.MatchName(a.ShortName, a.LongName, comparer));
2222
if (option == null) return NameLookupResult.NoOptionFound;
23-
return option.ConversionType == typeof(bool)
24-
? NameLookupResult.BooleanOptionFound
23+
return option.ConversionType == typeof(bool) || option.FlagCounter
24+
? NameLookupResult.FlagOptionFound
2525
: NameLookupResult.OtherOptionFound;
2626
}
2727

src/CommandLine/Core/OptionSpecification.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ sealed class OptionSpecification : Specification
1414
private readonly char separator;
1515
private readonly string setName;
1616
private readonly string group;
17+
private readonly bool flagCounter;
1718

1819
public OptionSpecification(string shortName, string longName, bool required, string setName, Maybe<int> min, Maybe<int> max,
1920
char separator, Maybe<object> defaultValue, string helpText, string metaValue, IEnumerable<string> enumValues,
20-
Type conversionType, TargetType targetType, string group, bool hidden = false)
21+
Type conversionType, TargetType targetType, string group, bool flagCounter, bool hidden)
2122
: base(SpecificationType.Option,
2223
required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, targetType, hidden)
2324
{
@@ -26,6 +27,7 @@ public OptionSpecification(string shortName, string longName, bool required, str
2627
this.separator = separator;
2728
this.setName = setName;
2829
this.group = group;
30+
this.flagCounter = flagCounter;
2931
}
3032

3133
public static OptionSpecification FromAttribute(OptionAttribute attribute, Type conversionType, IEnumerable<string> enumValues)
@@ -45,13 +47,14 @@ public static OptionSpecification FromAttribute(OptionAttribute attribute, Type
4547
conversionType,
4648
conversionType.ToTargetType(),
4749
attribute.Group,
50+
attribute.FlagCounter,
4851
attribute.Hidden);
4952
}
5053

51-
public static OptionSpecification NewSwitch(string shortName, string longName, bool required, string helpText, string metaValue, bool hidden = false)
54+
public static OptionSpecification NewSwitch(string shortName, string longName, bool required, string helpText, string metaValue, bool flagCounter, bool hidden)
5255
{
5356
return new OptionSpecification(shortName, longName, required, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(),
54-
'\0', Maybe.Nothing<object>(), helpText, metaValue, Enumerable.Empty<string>(), typeof(bool), TargetType.Switch, string.Empty, hidden);
57+
'\0', Maybe.Nothing<object>(), helpText, metaValue, Enumerable.Empty<string>(), typeof(bool), TargetType.Switch, string.Empty, flagCounter, hidden);
5558
}
5659

5760
public string ShortName
@@ -78,5 +81,10 @@ public string Group
7881
{
7982
get { return group; }
8083
}
84+
85+
public bool FlagCounter
86+
{
87+
get { return flagCounter; }
88+
}
8189
}
8290
}

src/CommandLine/Core/SpecificationExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public static OptionSpecification WithLongName(this OptionSpecification specific
3535
specification.ConversionType,
3636
specification.TargetType,
3737
specification.Group,
38+
specification.FlagCounter,
3839
specification.Hidden);
3940
}
4041

src/CommandLine/OptionAttribute.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public sealed class OptionAttribute : BaseAttribute
1717
private string setName;
1818
private char separator;
1919
private string group=string.Empty;
20+
private bool flagCounter;
2021

2122
private OptionAttribute(string shortName, string longName) : base()
2223
{
@@ -27,6 +28,7 @@ private OptionAttribute(string shortName, string longName) : base()
2728
this.longName = longName;
2829
setName = string.Empty;
2930
separator = '\0';
31+
flagCounter = false;
3032
}
3133

3234
/// <summary>
@@ -114,5 +116,14 @@ public string Group
114116
get { return group; }
115117
set { group = value; }
116118
}
119+
120+
/// <summary>
121+
/// When applied to an int property, turns that property into a count of how many times a boolean flag was applied (e.g., -vvv would become 3)
122+
/// </summary>
123+
public bool FlagCounter
124+
{
125+
get { return flagCounter; }
126+
set { flagCounter = value; }
127+
}
117128
}
118129
}

src/CommandLine/Text/HelpText.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,7 @@ private IEnumerable<Specification> AdaptVerbsToSpecifications(IEnumerable<Type>
856856
false,
857857
verbTuple.Item1.IsDefault? "(Default Verb) "+verbTuple.Item1.HelpText: verbTuple.Item1.HelpText, //Default verb
858858
string.Empty,
859+
false,
859860
verbTuple.Item1.Hidden);
860861
if (autoHelp)
861862
optionSpecs = optionSpecs.Concat(new[] { MakeHelpEntry() });
@@ -914,6 +915,7 @@ private OptionSpecification MakeHelpEntry()
914915
false,
915916
sentenceBuilder.HelpCommandText(AddDashesToOption),
916917
string.Empty,
918+
false,
917919
false);
918920
}
919921

@@ -925,6 +927,7 @@ private OptionSpecification MakeVersionEntry()
925927
false,
926928
sentenceBuilder.VersionCommandText(AddDashesToOption),
927929
string.Empty,
930+
false,
928931
false);
929932
}
930933

tests/CommandLine.Tests/Unit/Core/NameLookupTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public void Lookup_name_of_sequence_option_with_separator()
1717
// Fixture setup
1818
var expected = Maybe.Just(".");
1919
var specs = new[] { new OptionSpecification(string.Empty, "string-seq",
20-
false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '.', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty)};
20+
false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '.', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty, flagCounter:false, hidden:false)};
2121

2222
// Exercize system
2323
var result = NameLookup.HavingSeparator("string-seq", specs, StringComparer.Ordinal);
@@ -35,7 +35,7 @@ public void Get_name_from_option_specification()
3535

3636
// Fixture setup
3737
var expected = new NameInfo(ShortName, LongName);
38-
var spec = new OptionSpecification(ShortName, LongName, false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '.', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty);
38+
var spec = new OptionSpecification(ShortName, LongName, false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '.', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty, flagCounter:false, hidden:false);
3939

4040
// Exercize system
4141
var result = spec.FromOptionSpecification();

tests/CommandLine.Tests/Unit/Core/OptionMapperTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public void Map_boolean_switch_creates_boolean_value()
2828
var specProps = new[]
2929
{
3030
SpecificationProperty.Create(
31-
new OptionSpecification("x", string.Empty, false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', Maybe.Nothing<object>(), string.Empty, string.Empty, new List<string>(), typeof(bool), TargetType.Switch, string.Empty),
31+
new OptionSpecification("x", string.Empty, false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', Maybe.Nothing<object>(), string.Empty, string.Empty, new List<string>(), typeof(bool), TargetType.Switch, string.Empty, flagCounter: false, hidden:false),
3232
typeof(Simple_Options).GetProperties().Single(p => p.Name.Equals("BoolValue", StringComparison.Ordinal)),
3333
Maybe.Nothing<object>())
3434
};
@@ -64,7 +64,7 @@ public void Map_with_multi_instance_scalar()
6464
var specProps = new[]
6565
{
6666
SpecificationProperty.Create(
67-
new OptionSpecification("s", "shortandlong", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', Maybe.Nothing<object>(), string.Empty, string.Empty, new List<string>(), typeof(string), TargetType.Scalar, string.Empty),
67+
new OptionSpecification("s", "shortandlong", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', Maybe.Nothing<object>(), string.Empty, string.Empty, new List<string>(), typeof(string), TargetType.Scalar, string.Empty, flagCounter: false, hidden:false),
6868
typeof(Simple_Options).GetProperties().Single(p => p.Name.Equals(nameof(Simple_Options.ShortAndLong), StringComparison.Ordinal)),
6969
Maybe.Nothing<object>()),
7070
};
@@ -93,7 +93,7 @@ public void Map_with_multi_instance_sequence()
9393
var specProps = new[]
9494
{
9595
SpecificationProperty.Create(
96-
new OptionSpecification("i", string.Empty, false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', Maybe.Nothing<object>(), string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<int>), TargetType.Sequence, string.Empty),
96+
new OptionSpecification("i", string.Empty, false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', Maybe.Nothing<object>(), string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<int>), TargetType.Sequence, string.Empty, flagCounter: false, hidden:false),
9797
typeof(Simple_Options).GetProperties().Single(p => p.Name.Equals(nameof(Simple_Options.IntSequence), StringComparison.Ordinal)),
9898
Maybe.Nothing<object>())
9999
};
Lines changed: 58 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,58 @@
1-
using CommandLine.Core;
2-
using CommandLine.Tests.Fakes;
3-
using CSharpx;
4-
using System.Collections.Generic;
5-
using Xunit;
6-
7-
namespace CommandLine.Tests.Unit.Core
8-
{
9-
10-
public class SpecificationPropertyRulesTests
11-
{
12-
[Fact]
13-
public void Lookup_allows_multi_instance()
14-
{
15-
var tokens = new[]
16-
{
17-
Token.Name("name"),
18-
Token.Value("value"),
19-
Token.Name("name"),
20-
Token.Value("value2"),
21-
};
22-
23-
var specProps = new[]
24-
{
25-
SpecificationProperty.Create(
26-
new OptionSpecification(string.Empty, "name", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', Maybe.Nothing<object>(), string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty),
27-
typeof(SequenceOptions).GetProperty(nameof(SequenceOptions.StringSequence)),
28-
Maybe.Just(new object())),
29-
};
30-
31-
var results = specProps.Validate(SpecificationPropertyRules.Lookup(tokens, true));
32-
Assert.Empty(results);
33-
}
34-
35-
[Fact]
36-
public void Lookup_fails_with_repeated_options_false_multi_instance()
37-
{
38-
var tokens = new[]
39-
{
40-
Token.Name("name"),
41-
Token.Value("value"),
42-
Token.Name("name"),
43-
Token.Value("value2"),
44-
};
45-
46-
var specProps = new[]
47-
{
48-
SpecificationProperty.Create(
49-
new OptionSpecification(string.Empty, "name", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', Maybe.Nothing<object>(), string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty),
50-
typeof(SequenceOptions).GetProperty(nameof(SequenceOptions.StringSequence)),
51-
Maybe.Just(new object())),
52-
};
53-
54-
var results = specProps.Validate(SpecificationPropertyRules.Lookup(tokens, false));
55-
Assert.Contains(results, r => r.GetType() == typeof(RepeatedOptionError));
56-
}
57-
}
58-
}
1+
using CommandLine.Core;
2+
using CommandLine.Tests.Fakes;
3+
using CSharpx;
4+
using System.Collections.Generic;
5+
using Xunit;
6+
7+
namespace CommandLine.Tests.Unit.Core
8+
{
9+
10+
public class SpecificationPropertyRulesTests
11+
{
12+
[Fact]
13+
public void Lookup_allows_multi_instance()
14+
{
15+
var tokens = new[]
16+
{
17+
Token.Name("name"),
18+
Token.Value("value"),
19+
Token.Name("name"),
20+
Token.Value("value2"),
21+
};
22+
23+
var specProps = new[]
24+
{
25+
SpecificationProperty.Create(
26+
new OptionSpecification(string.Empty, "name", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', Maybe.Nothing<object>(), string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty, flagCounter: false, hidden:false),
27+
typeof(SequenceOptions).GetProperty(nameof(SequenceOptions.StringSequence)),
28+
Maybe.Just(new object())),
29+
};
30+
31+
var results = specProps.Validate(SpecificationPropertyRules.Lookup(tokens, true));
32+
Assert.Empty(results);
33+
}
34+
35+
[Fact]
36+
public void Lookup_fails_with_repeated_options_false_multi_instance()
37+
{
38+
var tokens = new[]
39+
{
40+
Token.Name("name"),
41+
Token.Value("value"),
42+
Token.Name("name"),
43+
Token.Value("value2"),
44+
};
45+
46+
var specProps = new[]
47+
{
48+
SpecificationProperty.Create(
49+
new OptionSpecification(string.Empty, "name", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', Maybe.Nothing<object>(), string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty, flagCounter: false, hidden:false),
50+
typeof(SequenceOptions).GetProperty(nameof(SequenceOptions.StringSequence)),
51+
Maybe.Just(new object())),
52+
};
53+
54+
var results = specProps.Validate(SpecificationPropertyRules.Lookup(tokens, false));
55+
Assert.Contains(results, r => r.GetType() == typeof(RepeatedOptionError));
56+
}
57+
}
58+
}

tests/CommandLine.Tests/Unit/Core/TokenPartitionerTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ public void Partition_sequence_returns_sequence()
2121
};
2222
var specs = new[]
2323
{
24-
new OptionSpecification(string.Empty, "stringvalue", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', null, string.Empty, string.Empty, new List<string>(), typeof(string), TargetType.Scalar, string.Empty),
25-
new OptionSpecification("i", string.Empty, false, string.Empty, Maybe.Just(3), Maybe.Just(4), '\0', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<int>), TargetType.Sequence, string.Empty)
24+
new OptionSpecification(string.Empty, "stringvalue", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', null, string.Empty, string.Empty, new List<string>(), typeof(string), TargetType.Scalar, string.Empty, flagCounter: false, hidden:false),
25+
new OptionSpecification("i", string.Empty, false, string.Empty, Maybe.Just(3), Maybe.Just(4), '\0', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<int>), TargetType.Sequence, string.Empty, flagCounter: false, hidden:false)
2626
};
2727

2828
// Exercize system
@@ -48,8 +48,8 @@ public void Partition_sequence_returns_sequence_with_duplicates()
4848
};
4949
var specs = new[]
5050
{
51-
new OptionSpecification(string.Empty, "stringvalue", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', null, string.Empty, string.Empty, new List<string>(), typeof(string), TargetType.Scalar, string.Empty),
52-
new OptionSpecification("i", string.Empty, false, string.Empty, Maybe.Just(3), Maybe.Just(4), '\0', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<int>), TargetType.Sequence, string.Empty)
51+
new OptionSpecification(string.Empty, "stringvalue", false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), '\0', null, string.Empty, string.Empty, new List<string>(), typeof(string), TargetType.Scalar, string.Empty, flagCounter: false, hidden:false),
52+
new OptionSpecification("i", string.Empty, false, string.Empty, Maybe.Just(3), Maybe.Just(4), '\0', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<int>), TargetType.Sequence, string.Empty, flagCounter:false, hidden:false)
5353
};
5454

5555
// Exercize system

tests/CommandLine.Tests/Unit/Core/TokenizerTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public void Explode_scalar_with_separator_in_odd_args_input_returns_sequence()
2121
var expectedTokens = new[] { Token.Name("i"), Token.Value("10"), Token.Name("string-seq"),
2222
Token.Value("aaa"), Token.Value("bb"), Token.Value("cccc"), Token.Name("switch") };
2323
var specs = new[] { new OptionSpecification(string.Empty, "string-seq",
24-
false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), ',', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty)};
24+
false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), ',', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty, flagCounter: false, hidden:false)};
2525

2626
// Exercize system
2727
var result =
@@ -44,7 +44,7 @@ public void Explode_scalar_with_separator_in_even_args_input_returns_sequence()
4444
var expectedTokens = new[] { Token.Name("x"), Token.Name("string-seq"),
4545
Token.Value("aaa"), Token.Value("bb"), Token.Value("cccc"), Token.Name("switch") };
4646
var specs = new[] { new OptionSpecification(string.Empty, "string-seq",
47-
false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), ',', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty)};
47+
false, string.Empty, Maybe.Nothing<int>(), Maybe.Nothing<int>(), ',', null, string.Empty, string.Empty, new List<string>(), typeof(IEnumerable<string>), TargetType.Sequence, string.Empty, flagCounter: false, hidden:false)};
4848

4949
// Exercize system
5050
var result =

0 commit comments

Comments
 (0)