Skip to content

Commit

Permalink
fix: Correctly handle dash count for built-in arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaeIDietrich committed Sep 12, 2022
1 parent 53ef250 commit 10432f1
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 17 deletions.
5 changes: 4 additions & 1 deletion src/CommandLine/Core/InstanceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public static ParserResult<T> Build<T>(
CultureInfo parsingCulture,
bool autoHelp,
bool autoVersion,
OptionsParseMode optionsParseMode,
IEnumerable<ErrorType> nonFatalErrors)
{
return Build(
Expand All @@ -34,6 +35,7 @@ public static ParserResult<T> Build<T>(
autoHelp,
autoVersion,
false,
optionsParseMode,
nonFatalErrors);
}

Expand All @@ -47,6 +49,7 @@ public static ParserResult<T> Build<T>(
bool autoHelp,
bool autoVersion,
bool allowMultiInstance,
OptionsParseMode optionsParseMode,
IEnumerable<ErrorType> nonFatalErrors) {
var typeInfo = factory.MapValueOrDefault(f => f().GetType(), typeof(T));

Expand Down Expand Up @@ -137,7 +140,7 @@ public static ParserResult<T> Build<T>(

var preprocessorErrors = (
argumentsList.Any()
? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer, autoHelp, autoVersion))
? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer, autoHelp, autoVersion, optionsParseMode))
: Enumerable.Empty<Error>()
).Memoize();

Expand Down
20 changes: 14 additions & 6 deletions src/CommandLine/Core/InstanceChooser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public static ParserResult<object> Choose(
CultureInfo parsingCulture,
bool autoHelp,
bool autoVersion,
OptionsParseMode optionsParseMode,
IEnumerable<ErrorType> nonFatalErrors)
{
return Choose(
Expand All @@ -33,6 +34,7 @@ public static ParserResult<object> Choose(
autoHelp,
autoVersion,
false,
optionsParseMode,
nonFatalErrors);
}

Expand All @@ -46,6 +48,7 @@ public static ParserResult<object> Choose(
bool autoHelp,
bool autoVersion,
bool allowMultiInstance,
OptionsParseMode optionsParseMode,
IEnumerable<ErrorType> nonFatalErrors)
{
var verbs = Verb.SelectFromTypes(types);
Expand All @@ -62,22 +65,23 @@ ParserResult<object> choose()
var firstArg = arguments.First();

bool preprocCompare(string command) =>
nameComparer.Equals(command, firstArg) ||
nameComparer.Equals(string.Concat("--", command), firstArg);
nameComparer.Equals(command, firstArg)
|| optionsParseMode != OptionsParseMode.SingleDashOnly && nameComparer.Equals(string.Concat("--", command), firstArg)
|| optionsParseMode != OptionsParseMode.Default && nameComparer.Equals(string.Concat("-", command), firstArg);

return (autoHelp && preprocCompare("help"))
? MakeNotParsed(types,
MakeHelpVerbRequestedError(verbs,
arguments.Skip(1).FirstOrDefault() ?? string.Empty, nameComparer))
: (autoVersion && preprocCompare("version"))
? MakeNotParsed(types, new VersionRequestedError())
: MatchVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, allowMultiInstance, nonFatalErrors);
: MatchVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, allowMultiInstance, optionsParseMode, nonFatalErrors);
}

return arguments.Any()
? choose()
: (defaultVerbCount == 1
? MatchDefaultVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, nonFatalErrors)
? MatchDefaultVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, optionsParseMode, nonFatalErrors)
: MakeNotParsed(types, new NoVerbSelectedError()));
}

Expand All @@ -91,6 +95,7 @@ private static ParserResult<object> MatchDefaultVerb(
CultureInfo parsingCulture,
bool autoHelp,
bool autoVersion,
OptionsParseMode optionsParseMode,
IEnumerable<ErrorType> nonFatalErrors)
{
return !(defaultVerb is null)
Expand All @@ -103,6 +108,7 @@ private static ParserResult<object> MatchDefaultVerb(
parsingCulture,
autoHelp,
autoVersion,
optionsParseMode,
nonFatalErrors)
: MakeNotParsed(verbs.Select(v => v.Item2), new BadVerbSelectedError(arguments.First()));
}
Expand All @@ -118,6 +124,7 @@ private static ParserResult<object> MatchVerb(
bool autoHelp,
bool autoVersion,
bool allowMultiInstance,
OptionsParseMode optionsParseMode,
IEnumerable<ErrorType> nonFatalErrors)
{
string firstArg = arguments.First();
Expand All @@ -129,7 +136,7 @@ private static ParserResult<object> MatchVerb(

if (verbUsed == default)
{
return MatchDefaultVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, nonFatalErrors);
return MatchDefaultVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, optionsParseMode, nonFatalErrors);
}
return InstanceBuilder.Build(
Maybe.Just<Func<object>>(
Expand All @@ -141,7 +148,8 @@ private static ParserResult<object> MatchVerb(
parsingCulture,
autoHelp,
autoVersion,
allowMultiInstance,
allowMultiInstance,
optionsParseMode,
nonFatalErrors);
}

Expand Down
17 changes: 9 additions & 8 deletions src/CommandLine/Core/PreprocessorGuards.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,31 @@ namespace CommandLine.Core
static class PreprocessorGuards
{
public static IEnumerable<Func<IEnumerable<string>, IEnumerable<Error>>>
Lookup(StringComparer nameComparer, bool autoHelp, bool autoVersion)
Lookup(StringComparer nameComparer, bool autoHelp, bool autoVersion, OptionsParseMode optionsParseMode)
{
var list = new List<Func<IEnumerable<string>, IEnumerable<Error>>>();
if (autoHelp)
list.Add(HelpCommand(nameComparer));
list.Add(HelpCommand(nameComparer, optionsParseMode));
if (autoVersion)
list.Add(VersionCommand(nameComparer));
list.Add(VersionCommand(nameComparer, optionsParseMode));
return list;
}

public static Func<IEnumerable<string>, IEnumerable<Error>> HelpCommand(StringComparer nameComparer)
public static Func<IEnumerable<string>, IEnumerable<Error>> HelpCommand(StringComparer nameComparer, OptionsParseMode optionsParseMode)
{
return
arguments =>
nameComparer.Equals("--help", arguments.First())
optionsParseMode != OptionsParseMode.SingleDashOnly && nameComparer.Equals("--help", arguments.First())
|| optionsParseMode != OptionsParseMode.Default && nameComparer.Equals("-help", arguments.First())
? new Error[] { new HelpRequestedError() }
: Enumerable.Empty<Error>();
}

public static Func<IEnumerable<string>, IEnumerable<Error>> VersionCommand(StringComparer nameComparer)
public static Func<IEnumerable<string>, IEnumerable<Error>> VersionCommand(StringComparer nameComparer, OptionsParseMode optionsParseMode)
{
return
arguments =>
nameComparer.Equals("--version", arguments.First())
optionsParseMode != OptionsParseMode.SingleDashOnly && nameComparer.Equals("--version", arguments.First())
|| optionsParseMode != OptionsParseMode.Default && nameComparer.Equals("-version", arguments.First())
? new Error[] { new VersionRequestedError() }
: Enumerable.Empty<Error>();
}
Expand Down
3 changes: 3 additions & 0 deletions src/CommandLine/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public ParserResult<T> ParseArguments<T>(IEnumerable<string> args)
settings.AutoHelp,
settings.AutoVersion,
settings.AllowMultiInstance,
settings.OptionsParseMode,
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
settings);
}
Expand Down Expand Up @@ -133,6 +134,7 @@ public ParserResult<T> ParseArguments<T>(Func<T> factory, IEnumerable<string> ar
settings.AutoHelp,
settings.AutoVersion,
settings.AllowMultiInstance,
settings.OptionsParseMode,
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
settings);
}
Expand Down Expand Up @@ -166,6 +168,7 @@ public ParserResult<object> ParseArguments(IEnumerable<string> args, params Type
settings.AutoHelp,
settings.AutoVersion,
settings.AllowMultiInstance,
settings.OptionsParseMode,
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
settings);
}
Expand Down
43 changes: 42 additions & 1 deletion tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace CommandLine.Tests.Unit.Core
{
public class InstanceBuilderTests
{
private static ParserResult<T> InvokeBuild<T>(string[] arguments, bool autoHelp = true, bool autoVersion = true, bool multiInstance = false)
private static ParserResult<T> InvokeBuild<T>(string[] arguments, bool autoHelp = true, bool autoVersion = true, bool multiInstance = false, OptionsParseMode optionsParseMode = OptionsParseMode.Default)
where T : new()
{
return InstanceBuilder.Build(
Expand All @@ -32,6 +32,7 @@ private static ParserResult<T> InvokeBuild<T>(string[] arguments, bool autoHelp
autoHelp,
autoVersion,
multiInstance,
optionsParseMode,
Enumerable.Empty<ErrorType>());
}

Expand All @@ -47,6 +48,7 @@ private static ParserResult<T> InvokeBuildEnumValuesCaseIgnore<T>(string[] argum
CultureInfo.InvariantCulture,
true,
true,
OptionsParseMode.Default,
Enumerable.Empty<ErrorType>());
}

Expand All @@ -61,6 +63,7 @@ private static ParserResult<T> InvokeBuildImmutable<T>(string[] arguments)
CultureInfo.InvariantCulture,
true,
true,
OptionsParseMode.Default,
Enumerable.Empty<ErrorType>());
}

Expand Down Expand Up @@ -421,6 +424,7 @@ public void Double_dash_force_subsequent_arguments_as_values()
CultureInfo.InvariantCulture,
true,
true,
OptionsParseMode.Default,
Enumerable.Empty<ErrorType>());

// Verify outcome
Expand Down Expand Up @@ -1263,6 +1267,43 @@ public void Parse_int_sequence_with_multi_instance()

((Parsed<Options_With_Sequence>)result).Value.IntSequence.Should().BeEquivalentTo(expected);
}

[Theory]
[InlineData("-help", OptionsParseMode.SingleDashOnly)]
[InlineData("-help", OptionsParseMode.SingleOrDoubleDash)]
[InlineData("--help", OptionsParseMode.Default)]
[InlineData("--help", OptionsParseMode.SingleOrDoubleDash)]
public void Parse_Built_In_Help_Argument(string argument, OptionsParseMode optionsParseMode)
{
var result = InvokeBuild<Simple_Options>(new[] { argument }, optionsParseMode: optionsParseMode);

result.Errors.Single().Should().BeOfType<HelpRequestedError>();
}

[Theory]
[InlineData("-version", OptionsParseMode.SingleDashOnly)]
[InlineData("-version", OptionsParseMode.SingleOrDoubleDash)]
[InlineData("--version", OptionsParseMode.Default)]
[InlineData("--version", OptionsParseMode.SingleOrDoubleDash)]
public void Parse_Built_In_Version_Argument(string argument, OptionsParseMode optionsParseMode)
{
var result = InvokeBuild<Simple_Options>(new[] { argument }, optionsParseMode: optionsParseMode);

result.Errors.Single().Should().BeOfType<VersionRequestedError>();
}

[Theory]
[InlineData("-help", OptionsParseMode.Default)]
[InlineData("--help", OptionsParseMode.SingleDashOnly)]
[InlineData("-version", OptionsParseMode.Default)]
[InlineData("--version", OptionsParseMode.SingleDashOnly)]
public void Parse_Invalid_Built_In_Argument(string argument, OptionsParseMode optionsParseMode)
{
var result = InvokeBuild<Simple_Options>(new[] { argument }, optionsParseMode: optionsParseMode);

result.Errors.Should().NotBeOfType<HelpVerbRequestedError>()
.And.Should().NotBeOfType<VersionRequestedError>();
}

#region custom types

Expand Down
59 changes: 58 additions & 1 deletion tests/CommandLine.Tests/Unit/Core/InstanceChooserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public class InstanceChooserTests
private static ParserResult<object> InvokeChoose(
IEnumerable<Type> types,
IEnumerable<string> arguments,
bool multiInstance = false)
bool multiInstance = false,
OptionsParseMode optionsParseMode = OptionsParseMode.Default)
{
return InstanceChooser.Choose(
(args, optionSpecs) => Tokenizer.ConfigureTokenizer(StringComparer.Ordinal, false, false)(args, optionSpecs),
Expand All @@ -28,6 +29,7 @@ private static ParserResult<object> InvokeChoose(
true,
true,
multiInstance,
optionsParseMode,
Enumerable.Empty<ErrorType>());
}

Expand Down Expand Up @@ -183,5 +185,60 @@ public void Parse_sequence_verb_with_multi_instance_returns_verb_instance()
Assert.IsType<SequenceOptions>(((Parsed<object>)result).Value);
expected.Should().BeEquivalentTo(((Parsed<object>)result).Value);
}

[Theory]
[InlineData("help", OptionsParseMode.Default)]
[InlineData("help", OptionsParseMode.SingleDashOnly)]
[InlineData("help", OptionsParseMode.SingleOrDoubleDash)]
[InlineData("-help", OptionsParseMode.SingleDashOnly)]
[InlineData("-help", OptionsParseMode.SingleOrDoubleDash)]
[InlineData("--help", OptionsParseMode.Default)]
[InlineData("--help", OptionsParseMode.SingleOrDoubleDash)]
public void Parse_Built_In_Help_Argument(string argument, OptionsParseMode optionsParseMode)
{
var result = InvokeChoose(
Type.EmptyTypes,
new[] { argument },
true,
optionsParseMode);

result.Errors.Single().Should().BeOfType<HelpVerbRequestedError>();
}

[Theory]
[InlineData("version", OptionsParseMode.Default)]
[InlineData("version", OptionsParseMode.SingleDashOnly)]
[InlineData("version", OptionsParseMode.SingleOrDoubleDash)]
[InlineData("-version", OptionsParseMode.SingleDashOnly)]
[InlineData("-version", OptionsParseMode.SingleOrDoubleDash)]
[InlineData("--version", OptionsParseMode.Default)]
[InlineData("--version", OptionsParseMode.SingleOrDoubleDash)]
public void Parse_Built_In_Version_Argument(string argument, OptionsParseMode optionsParseMode)
{
var result = InvokeChoose(
Type.EmptyTypes,
new[] { argument },
true,
optionsParseMode);

result.Errors.Single().Should().BeOfType<VersionRequestedError>();
}

[Theory]
[InlineData("-help", OptionsParseMode.Default)]
[InlineData("--help", OptionsParseMode.SingleDashOnly)]
[InlineData("-version", OptionsParseMode.Default)]
[InlineData("--version", OptionsParseMode.SingleDashOnly)]
public void Parse_Invalid_Built_In_Argument(string argument, OptionsParseMode optionsParseMode)
{
var result = InvokeChoose(
Type.EmptyTypes,
new[] { argument },
true,
optionsParseMode);

result.Errors.Single().Should().NotBeOfType<HelpVerbRequestedError>()
.And.Should().NotBeOfType<VersionRequestedError>();
}
}
}

0 comments on commit 10432f1

Please sign in to comment.