Skip to content

add CaseInsensitiveValues param to ParserSettings #289

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/CommandLine/Core/InstanceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public static ParserResult<T> Build<T>(
Func<IEnumerable<string>, IEnumerable<OptionSpecification>, Result<IEnumerable<Token>, Error>> tokenizer,
IEnumerable<string> arguments,
StringComparer nameComparer,
bool ignoreValueCase,
CultureInfo parsingCulture,
IEnumerable<ErrorType> nonFatalErrors)
{
Expand Down Expand Up @@ -57,14 +58,14 @@ public static ParserResult<T> Build<T>(
OptionMapper.MapValues(
(from pt in specProps where pt.Specification.IsOption() select pt),
optionsPartition,
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture),
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture, ignoreValueCase),
nameComparer);

var valueSpecPropsResult =
ValueMapper.MapValues(
(from pt in specProps where pt.Specification.IsValue() select pt),
valuesPartition,
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture));
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture, ignoreValueCase));

var missingValueErrors = from token in errorsPartition
select
Expand Down
1 change: 1 addition & 0 deletions src/CommandLine/Core/InstanceChooser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ private static ParserResult<object> MatchVerb(
tokenizer,
arguments.Skip(1),
nameComparer,
false,
parsingCulture,
nonFatalErrors)
: MakeNotParsed(verbs.Select(v => v.Item2), new BadVerbSelectedError(arguments.First()));
Expand Down
22 changes: 11 additions & 11 deletions src/CommandLine/Core/TypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ namespace CommandLine.Core
{
static class TypeConverter
{
public static Maybe<object> ChangeType(IEnumerable<string> values, Type conversionType, bool scalar, CultureInfo conversionCulture)
public static Maybe<object> ChangeType(IEnumerable<string> values, Type conversionType, bool scalar, CultureInfo conversionCulture, bool ignoreValueCase)
{
return scalar
? ChangeTypeScalar(values.Single(), conversionType, conversionCulture)
: ChangeTypeSequence(values, conversionType, conversionCulture);
? ChangeTypeScalar(values.Single(), conversionType, conversionCulture, ignoreValueCase)
: ChangeTypeSequence(values, conversionType, conversionCulture, ignoreValueCase);
}

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

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

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

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

private static Result<object, Exception> ChangeTypeScalarImpl(string value, Type conversionType, CultureInfo conversionCulture)
private static Result<object, Exception> ChangeTypeScalarImpl(string value, Type conversionType, CultureInfo conversionCulture, bool ignoreValueCase)
{
Func<object> changeType = () =>
{
Expand Down Expand Up @@ -72,7 +72,7 @@ private static Result<object, Exception> ChangeTypeScalarImpl(string value, Type

return value.IsBooleanString()
? value.ToBoolean() : conversionType.IsEnum
? value.ToEnum(conversionType) : safeChangeType();
? value.ToEnum(conversionType, ignoreValueCase) : safeChangeType();
};

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

private static object ToEnum(this string value, Type conversionType)
private static object ToEnum(this string value, Type conversionType, bool ignoreValueCase)
{
object parsedValue;
try
{
parsedValue = Enum.Parse(conversionType, value);
parsedValue = Enum.Parse(conversionType, value, ignoreValueCase);
}
catch (ArgumentException)
{
Expand Down
2 changes: 2 additions & 0 deletions src/CommandLine/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public ParserResult<T> ParseArguments<T>(IEnumerable<string> args)
(arguments, optionSpecs) => Tokenize(arguments, optionSpecs, settings),
args,
settings.NameComparer,
settings.CaseInsensitiveEnumValues,
settings.ParsingCulture,
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
settings);
Expand Down Expand Up @@ -125,6 +126,7 @@ public ParserResult<T> ParseArguments<T>(Func<T> factory, IEnumerable<string> ar
(arguments, optionSpecs) => Tokenize(arguments, optionSpecs, settings),
args,
settings.NameComparer,
settings.CaseInsensitiveEnumValues,
settings.ParsingCulture,
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
settings);
Expand Down
12 changes: 12 additions & 0 deletions src/CommandLine/ParserSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class ParserSettings : IDisposable
{
private bool disposed;
private bool caseSensitive;
private bool caseInsensitiveEnumValues;
private TextWriter helpWriter;
private bool ignoreUnknownArguments;
private CultureInfo parsingCulture;
Expand All @@ -26,6 +27,7 @@ public class ParserSettings : IDisposable
public ParserSettings()
{
caseSensitive = true;
caseInsensitiveEnumValues = false;
parsingCulture = CultureInfo.InvariantCulture;
}

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

/// <summary>
/// Gets or sets a value indicating whether perform case sensitive comparisons of <i>values</i>.
/// Note that case insensitivity only applies to <i>values</i>, not the parameters.
/// </summary>
public bool CaseInsensitiveEnumValues
{
get { return caseInsensitiveEnumValues; }
set { PopsicleSetter.Set(Consumed, ref caseInsensitiveEnumValues, value); }
}

/// <summary>
/// Gets or sets the culture used when parsing arguments to typed properties.
/// </summary>
Expand Down
37 changes: 37 additions & 0 deletions tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ private static ParserResult<T> InvokeBuild<T>(string[] arguments)
(args, optionSpecs) => Tokenizer.ConfigureTokenizer(StringComparer.Ordinal, false, false)(args, optionSpecs),
arguments,
StringComparer.Ordinal,
false,
CultureInfo.InvariantCulture,
Enumerable.Empty<ErrorType>());
}

private static ParserResult<T> InvokeBuildEnumValuesCaseIgnore<T>(string[] arguments)
where T : new()
{
return InstanceBuilder.Build(
Maybe.Just<Func<T>>(() => new T()),
(args, optionSpecs) => Tokenizer.ConfigureTokenizer(StringComparer.Ordinal, false, false)(args, optionSpecs),
arguments,
StringComparer.Ordinal,
true,
CultureInfo.InvariantCulture,
Enumerable.Empty<ErrorType>());
}
Expand All @@ -36,6 +50,7 @@ private static ParserResult<T> InvokeBuildImmutable<T>(string[] arguments)
(args, optionSpecs) => Tokenizer.ConfigureTokenizer(StringComparer.Ordinal, false, false)(args, optionSpecs),
arguments,
StringComparer.Ordinal,
false,
CultureInfo.InvariantCulture,
Enumerable.Empty<ErrorType>());
}
Expand Down Expand Up @@ -259,6 +274,27 @@ public void Parse_enum_value(string[] arguments, Colors expected)
// Teardown
}

[Theory]
[InlineData(new[] { "--colors", "red" }, Colors.Red)]
[InlineData(new[] { "--colors", "green" }, Colors.Green)]
[InlineData(new[] { "--colors", "blue" }, Colors.Blue)]
[InlineData(new[] { "--colors", "0" }, Colors.Red)]
[InlineData(new[] { "--colors", "1" }, Colors.Green)]
[InlineData(new[] { "--colors", "2" }, Colors.Blue)]
public void Parse_enum_value_ignore_case(string[] arguments, Colors expected)
{
// Fixture setup in attribute

// Exercize system
var result = InvokeBuildEnumValuesCaseIgnore<Simple_Options_With_Enum>(
arguments);

// Verify outcome
expected.ShouldBeEquivalentTo(((Parsed<Simple_Options_With_Enum>)result).Value.Colors);

// Teardown
}

[Fact]
public void Parse_enum_value_with_wrong_index_generates_BadFormatConversionError()
{
Expand Down Expand Up @@ -411,6 +447,7 @@ public void Double_dash_force_subsequent_arguments_as_values()
args => Tokenizer.Tokenize(args, name => NameLookup.Contains(name, optionSpecs, StringComparer.Ordinal))),
arguments,
StringComparer.Ordinal,
false,
CultureInfo.InvariantCulture,
Enumerable.Empty<ErrorType>());

Expand Down
2 changes: 1 addition & 1 deletion tests/CommandLine.Tests/Unit/Core/OptionMapperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public void Map_boolean_switch_creates_boolean_value()
var result = OptionMapper.MapValues(
specProps.Where(pt => pt.Specification.IsOption()),
tokenPartitions,
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, CultureInfo.InvariantCulture),
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, CultureInfo.InvariantCulture, false),
StringComparer.InvariantCulture);

// Verify outcome
Expand Down