diff --git a/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs b/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs index 51590d5990..ce608f65bc 100644 --- a/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs +++ b/src/Shared/CommandLineUtils/CommandLine/CommandLineApplication.cs @@ -26,10 +26,13 @@ namespace Microsoft.Extensions.CommandLineUtils // options. private readonly bool _continueAfterUnexpectedArg; - public CommandLineApplication(bool throwOnUnexpectedArg = true, bool continueAfterUnexpectedArg = false) + private readonly bool _treatUnmatchedOptionsAsArguments; + + public CommandLineApplication(bool throwOnUnexpectedArg = true, bool continueAfterUnexpectedArg = false, bool treatUnmatchedOptionsAsArguments = false) { _throwOnUnexpectedArg = throwOnUnexpectedArg; _continueAfterUnexpectedArg = continueAfterUnexpectedArg; + _treatUnmatchedOptionsAsArguments = treatUnmatchedOptionsAsArguments; Options = new List(); Arguments = new List(); Commands = new List(); @@ -136,6 +139,7 @@ namespace Microsoft.Extensions.CommandLineUtils CommandLineApplication command = this; CommandOption option = null; IEnumerator arguments = null; + var argumentsAssigned = false; for (var index = 0; index < args.Length; index++) { @@ -161,6 +165,25 @@ namespace Microsoft.Extensions.CommandLineUtils var longOptionName = longOption[0]; option = command.GetOptions().SingleOrDefault(opt => string.Equals(opt.LongName, longOptionName, StringComparison.Ordinal)); + if (option == null && _treatUnmatchedOptionsAsArguments) + { + if (arguments == null) + { + arguments = new CommandArgumentEnumerator(command.Arguments.GetEnumerator()); + } + if (arguments.MoveNext()) + { + processed = true; + arguments.Current.Values.Add(arg); + argumentsAssigned = true; + continue; + } + //else + //{ + // argumentsAssigned = false; + //} + } + if (option == null) { var ignoreContinueAfterUnexpectedArg = false; @@ -221,6 +244,25 @@ namespace Microsoft.Extensions.CommandLineUtils processed = true; option = command.GetOptions().SingleOrDefault(opt => string.Equals(opt.ShortName, shortOption[0], StringComparison.Ordinal)); + if (option == null && _treatUnmatchedOptionsAsArguments) + { + if (arguments == null) + { + arguments = new CommandArgumentEnumerator(command.Arguments.GetEnumerator()); + } + if (arguments.MoveNext()) + { + processed = true; + arguments.Current.Values.Add(arg); + argumentsAssigned = true; + continue; + } + //else + //{ + // argumentsAssigned = false; + //} + } + // If not a short option, try symbol option if (option == null) { @@ -278,7 +320,7 @@ namespace Microsoft.Extensions.CommandLineUtils option = null; } - if (!processed && arguments == null) + if (!processed && !argumentsAssigned) { var currentCommand = command; foreach (var subcommand in command.Commands) diff --git a/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs b/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs index cc6d0d841e..0bdc4a8f1d 100644 --- a/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs +++ b/src/Shared/test/Shared.Tests/CommandLineApplicationTests.cs @@ -1166,5 +1166,59 @@ Examples: Assert.Equal($"Unrecognized option '{inputOption}'", exception.Message); } + + [Fact] + public void TreatUnmatchedOptionsAsArguments() + { + CommandArgument first = null; + CommandArgument second = null; + + CommandOption firstOption = null; + CommandOption secondOption = null; + + var firstUnmatchedOption = "-firstUnmatchedOption"; + var firstActualOption = "-firstActualOption"; + var seconUnmatchedOption = "--secondUnmatchedOption"; + var secondActualOption = "--secondActualOption"; + + var app = new CommandLineApplication(treatUnmatchedOptionsAsArguments: true); + + app.Command("test", c => + { + firstOption = c.Option("-firstActualOption", "first option", CommandOptionType.NoValue); + secondOption = c.Option("--secondActualOption", "second option", CommandOptionType.NoValue); + + first = c.Argument("first", "First argument"); + second = c.Argument("second", "Second argument"); + c.OnExecute(() => 0); + }); + + app.Execute("test", firstUnmatchedOption, firstActualOption, seconUnmatchedOption, secondActualOption); + + Assert.Equal(firstUnmatchedOption, first.Value); + Assert.Equal(seconUnmatchedOption, second.Value); + + Assert.Equal(firstActualOption, firstOption.Template); + Assert.Equal(secondActualOption, secondOption.Template); + } + + [Fact] + public void ThrowExceptionWhenUnmatchedOptionAndTreatUnmatchedOptionsAsArgumentsIsFalse() + { + CommandArgument first = null; + + var firstOption = "-firstUnmatchedOption"; + + var app = new CommandLineApplication(treatUnmatchedOptionsAsArguments: false); + app.Command("test", c => + { + first = c.Argument("first", "First argument"); + c.OnExecute(() => 0); + }); + + var exception = Assert.Throws(() => app.Execute("test", firstOption)); + + Assert.Equal($"Unrecognized option '{firstOption}'", exception.Message); + } } }