Skip to content

Commit 0750d78

Browse files
committed
Merge pull request #289 from regme/case-insensetive-values
add CaseInsensitiveValues param to ParserSettings
2 parents 6373ddb + 07d7344 commit 0750d78

File tree

7 files changed

+67
-14
lines changed

7 files changed

+67
-14
lines changed

src/CommandLine/Core/InstanceBuilder.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public static ParserResult<T> Build<T>(
1717
Func<IEnumerable<string>, IEnumerable<OptionSpecification>, Result<IEnumerable<Token>, Error>> tokenizer,
1818
IEnumerable<string> arguments,
1919
StringComparer nameComparer,
20+
bool ignoreValueCase,
2021
CultureInfo parsingCulture,
2122
IEnumerable<ErrorType> nonFatalErrors)
2223
{
@@ -57,14 +58,14 @@ public static ParserResult<T> Build<T>(
5758
OptionMapper.MapValues(
5859
(from pt in specProps where pt.Specification.IsOption() select pt),
5960
optionsPartition,
60-
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture),
61+
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture, ignoreValueCase),
6162
nameComparer);
6263

6364
var valueSpecPropsResult =
6465
ValueMapper.MapValues(
6566
(from pt in specProps where pt.Specification.IsValue() select pt),
6667
valuesPartition,
67-
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture));
68+
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture, ignoreValueCase));
6869

6970
var missingValueErrors = from token in errorsPartition
7071
select

src/CommandLine/Core/InstanceChooser.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ private static ParserResult<object> MatchVerb(
6060
tokenizer,
6161
arguments.Skip(1),
6262
nameComparer,
63+
false,
6364
parsingCulture,
6465
nonFatalErrors)
6566
: MakeNotParsed(verbs.Select(v => v.Item2), new BadVerbSelectedError(arguments.First()));

src/CommandLine/Core/TypeConverter.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ namespace CommandLine.Core
1212
{
1313
static class TypeConverter
1414
{
15-
public static Maybe<object> ChangeType(IEnumerable<string> values, Type conversionType, bool scalar, CultureInfo conversionCulture)
15+
public static Maybe<object> ChangeType(IEnumerable<string> values, Type conversionType, bool scalar, CultureInfo conversionCulture, bool ignoreValueCase)
1616
{
1717
return scalar
18-
? ChangeTypeScalar(values.Single(), conversionType, conversionCulture)
19-
: ChangeTypeSequence(values, conversionType, conversionCulture);
18+
? ChangeTypeScalar(values.Single(), conversionType, conversionCulture, ignoreValueCase)
19+
: ChangeTypeSequence(values, conversionType, conversionCulture, ignoreValueCase);
2020
}
2121

22-
private static Maybe<object> ChangeTypeSequence(IEnumerable<string> values, Type conversionType, CultureInfo conversionCulture)
22+
private static Maybe<object> ChangeTypeSequence(IEnumerable<string> values, Type conversionType, CultureInfo conversionCulture, bool ignoreValueCase)
2323
{
2424
var type =
2525
conversionType.GetGenericArguments()
@@ -28,22 +28,22 @@ private static Maybe<object> ChangeTypeSequence(IEnumerable<string> values, Type
2828
.FromJustOrFail(
2929
new ApplicationException("Non scalar properties should be sequence of type IEnumerable<T>."));
3030

31-
var converted = values.Select(value => ChangeTypeScalar(value, type, conversionCulture));
31+
var converted = values.Select(value => ChangeTypeScalar(value, type, conversionCulture, ignoreValueCase));
3232

3333
return converted.Any(a => a.MatchNothing())
3434
? Maybe.Nothing<object>()
3535
: Maybe.Just(converted.Select(c => ((Just<object>)c).Value).ToUntypedArray(type));
3636
}
3737

38-
private static Maybe<object> ChangeTypeScalar(string value, Type conversionType, CultureInfo conversionCulture)
38+
private static Maybe<object> ChangeTypeScalar(string value, Type conversionType, CultureInfo conversionCulture, bool ignoreValueCase)
3939
{
40-
var result = ChangeTypeScalarImpl(value, conversionType, conversionCulture);
40+
var result = ChangeTypeScalarImpl(value, conversionType, conversionCulture, ignoreValueCase);
4141
result.Match((_,__) => { }, e => e.First().RethrowWhenAbsentIn(
4242
new[] { typeof(InvalidCastException), typeof(FormatException), typeof(OverflowException) }));
4343
return result.ToMaybe();
4444
}
4545

46-
private static Result<object, Exception> ChangeTypeScalarImpl(string value, Type conversionType, CultureInfo conversionCulture)
46+
private static Result<object, Exception> ChangeTypeScalarImpl(string value, Type conversionType, CultureInfo conversionCulture, bool ignoreValueCase)
4747
{
4848
Func<object> changeType = () =>
4949
{
@@ -72,7 +72,7 @@ private static Result<object, Exception> ChangeTypeScalarImpl(string value, Type
7272

7373
return value.IsBooleanString()
7474
? value.ToBoolean() : conversionType.IsEnum
75-
? value.ToEnum(conversionType) : safeChangeType();
75+
? value.ToEnum(conversionType, ignoreValueCase) : safeChangeType();
7676
};
7777

7878
Func<object> makeType = () =>
@@ -94,12 +94,12 @@ private static Result<object, Exception> ChangeTypeScalarImpl(string value, Type
9494
: makeType);
9595
}
9696

97-
private static object ToEnum(this string value, Type conversionType)
97+
private static object ToEnum(this string value, Type conversionType, bool ignoreValueCase)
9898
{
9999
object parsedValue;
100100
try
101101
{
102-
parsedValue = Enum.Parse(conversionType, value);
102+
parsedValue = Enum.Parse(conversionType, value, ignoreValueCase);
103103
}
104104
catch (ArgumentException)
105105
{

src/CommandLine/Parser.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public ParserResult<T> ParseArguments<T>(IEnumerable<string> args)
9797
(arguments, optionSpecs) => Tokenize(arguments, optionSpecs, settings),
9898
args,
9999
settings.NameComparer,
100+
settings.CaseInsensitiveEnumValues,
100101
settings.ParsingCulture,
101102
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
102103
settings);
@@ -125,6 +126,7 @@ public ParserResult<T> ParseArguments<T>(Func<T> factory, IEnumerable<string> ar
125126
(arguments, optionSpecs) => Tokenize(arguments, optionSpecs, settings),
126127
args,
127128
settings.NameComparer,
129+
settings.CaseInsensitiveEnumValues,
128130
settings.ParsingCulture,
129131
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
130132
settings);

src/CommandLine/ParserSettings.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class ParserSettings : IDisposable
1515
{
1616
private bool disposed;
1717
private bool caseSensitive;
18+
private bool caseInsensitiveEnumValues;
1819
private TextWriter helpWriter;
1920
private bool ignoreUnknownArguments;
2021
private CultureInfo parsingCulture;
@@ -26,6 +27,7 @@ public class ParserSettings : IDisposable
2627
public ParserSettings()
2728
{
2829
caseSensitive = true;
30+
caseInsensitiveEnumValues = false;
2931
parsingCulture = CultureInfo.InvariantCulture;
3032
}
3133

@@ -48,6 +50,16 @@ public bool CaseSensitive
4850
set { PopsicleSetter.Set(Consumed, ref caseSensitive, value); }
4951
}
5052

53+
/// <summary>
54+
/// Gets or sets a value indicating whether perform case sensitive comparisons of <i>values</i>.
55+
/// Note that case insensitivity only applies to <i>values</i>, not the parameters.
56+
/// </summary>
57+
public bool CaseInsensitiveEnumValues
58+
{
59+
get { return caseInsensitiveEnumValues; }
60+
set { PopsicleSetter.Set(Consumed, ref caseInsensitiveEnumValues, value); }
61+
}
62+
5163
/// <summary>
5264
/// Gets or sets the culture used when parsing arguments to typed properties.
5365
/// </summary>

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,20 @@ private static ParserResult<T> InvokeBuild<T>(string[] arguments)
2525
(args, optionSpecs) => Tokenizer.ConfigureTokenizer(StringComparer.Ordinal, false, false)(args, optionSpecs),
2626
arguments,
2727
StringComparer.Ordinal,
28+
false,
29+
CultureInfo.InvariantCulture,
30+
Enumerable.Empty<ErrorType>());
31+
}
32+
33+
private static ParserResult<T> InvokeBuildEnumValuesCaseIgnore<T>(string[] arguments)
34+
where T : new()
35+
{
36+
return InstanceBuilder.Build(
37+
Maybe.Just<Func<T>>(() => new T()),
38+
(args, optionSpecs) => Tokenizer.ConfigureTokenizer(StringComparer.Ordinal, false, false)(args, optionSpecs),
39+
arguments,
40+
StringComparer.Ordinal,
41+
true,
2842
CultureInfo.InvariantCulture,
2943
Enumerable.Empty<ErrorType>());
3044
}
@@ -36,6 +50,7 @@ private static ParserResult<T> InvokeBuildImmutable<T>(string[] arguments)
3650
(args, optionSpecs) => Tokenizer.ConfigureTokenizer(StringComparer.Ordinal, false, false)(args, optionSpecs),
3751
arguments,
3852
StringComparer.Ordinal,
53+
false,
3954
CultureInfo.InvariantCulture,
4055
Enumerable.Empty<ErrorType>());
4156
}
@@ -259,6 +274,27 @@ public void Parse_enum_value(string[] arguments, Colors expected)
259274
// Teardown
260275
}
261276

277+
[Theory]
278+
[InlineData(new[] { "--colors", "red" }, Colors.Red)]
279+
[InlineData(new[] { "--colors", "green" }, Colors.Green)]
280+
[InlineData(new[] { "--colors", "blue" }, Colors.Blue)]
281+
[InlineData(new[] { "--colors", "0" }, Colors.Red)]
282+
[InlineData(new[] { "--colors", "1" }, Colors.Green)]
283+
[InlineData(new[] { "--colors", "2" }, Colors.Blue)]
284+
public void Parse_enum_value_ignore_case(string[] arguments, Colors expected)
285+
{
286+
// Fixture setup in attribute
287+
288+
// Exercize system
289+
var result = InvokeBuildEnumValuesCaseIgnore<Simple_Options_With_Enum>(
290+
arguments);
291+
292+
// Verify outcome
293+
expected.ShouldBeEquivalentTo(((Parsed<Simple_Options_With_Enum>)result).Value.Colors);
294+
295+
// Teardown
296+
}
297+
262298
[Fact]
263299
public void Parse_enum_value_with_wrong_index_generates_BadFormatConversionError()
264300
{
@@ -411,6 +447,7 @@ public void Double_dash_force_subsequent_arguments_as_values()
411447
args => Tokenizer.Tokenize(args, name => NameLookup.Contains(name, optionSpecs, StringComparer.Ordinal))),
412448
arguments,
413449
StringComparer.Ordinal,
450+
false,
414451
CultureInfo.InvariantCulture,
415452
Enumerable.Empty<ErrorType>());
416453

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void Map_boolean_switch_creates_boolean_value()
3434
var result = OptionMapper.MapValues(
3535
specProps.Where(pt => pt.Specification.IsOption()),
3636
tokenPartitions,
37-
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, CultureInfo.InvariantCulture),
37+
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, CultureInfo.InvariantCulture, false),
3838
StringComparer.InvariantCulture);
3939

4040
// Verify outcome

0 commit comments

Comments
 (0)