From 3206f7b16a302c76052e84405ec46ab8f5e2053d Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Sat, 18 Feb 2023 21:31:02 +0100 Subject: [PATCH 01/13] remove Argument.Parse as it has side effects: creating RootCommand and setting it as a Parent of given Argument --- src/System.CommandLine.Tests/ArgumentTests.cs | 18 +++++++++--------- .../Binding/TypeConversionTests.cs | 2 +- .../ParsingValidationTests.cs | 2 +- src/System.CommandLine.Tests/SymbolTests.cs | 19 ------------------- src/System.CommandLine/Argument.cs | 17 ----------------- 5 files changed, 11 insertions(+), 47 deletions(-) diff --git a/src/System.CommandLine.Tests/ArgumentTests.cs b/src/System.CommandLine.Tests/ArgumentTests.cs index a32a0afa71..36de8980f9 100644 --- a/src/System.CommandLine.Tests/ArgumentTests.cs +++ b/src/System.CommandLine.Tests/ArgumentTests.cs @@ -128,7 +128,7 @@ public void Validation_failure_message_can_be_specified_when_parsing_tokens() return null; }); - argument.Parse("x") + new RootCommand { argument }.Parse("x") .Errors .Should() .ContainSingle(e => ((ArgumentResult)e.SymbolResult).Argument == argument) @@ -147,7 +147,7 @@ public void Validation_failure_message_can_be_specified_when_evaluating_default_ return null; }, true); - argument.Parse("") + new RootCommand { argument }.Parse("") .Errors .Should() .ContainSingle(e => ((ArgumentResult)e.SymbolResult).Argument == argument) @@ -183,7 +183,7 @@ public void custom_parsing_of_scalar_value_from_an_argument_with_one_token() { var argument = new Argument(result => int.Parse(result.Tokens.Single().Value)); - argument.Parse("123") + new RootCommand { argument }.Parse("123") .GetValue(argument) .Should() .Be(123); @@ -194,7 +194,7 @@ public void custom_parsing_of_sequence_value_from_an_argument_with_one_token() { var argument = new Argument>(result => result.Tokens.Single().Value.Split(',').Select(int.Parse)); - argument.Parse("1,2,3") + new RootCommand { argument }.Parse("1,2,3") .GetValue(argument) .Should() .BeEquivalentTo(new[] { 1, 2, 3 }); @@ -208,7 +208,7 @@ public void custom_parsing_of_sequence_value_from_an_argument_with_multiple_toke return result.Tokens.Select(t => int.Parse(t.Value)).ToArray(); }); - argument.Parse("1 2 3") + new RootCommand { argument }.Parse("1 2 3") .GetValue(argument) .Should() .BeEquivalentTo(new[] { 1, 2, 3 }); @@ -222,7 +222,7 @@ public void custom_parsing_of_scalar_value_from_an_argument_with_multiple_tokens Arity = ArgumentArity.ZeroOrMore }; - argument.Parse("1 2 3") + new RootCommand { argument }.Parse("1 2 3") .GetValue(argument) .Should() .Be(6); @@ -375,7 +375,7 @@ public void Default_value_and_custom_argument_parser_can_be_used_together() var argument = new Argument(_ => 789, true); argument.SetDefaultValue(123); - var result = argument.Parse(""); + var result = new RootCommand { argument }.Parse(""); result.GetValue(argument) .Should() @@ -653,7 +653,7 @@ public void OnlyTake_throws_when_called_with_a_negative_value() return null; }); - argument.Invoking(a => a.Parse("1 2 3")) + argument.Invoking(a => new RootCommand { a }.Parse("1 2 3")) .Should() .Throw() .Which @@ -675,7 +675,7 @@ public void OnlyTake_throws_when_called_twice() return null; }); - argument.Invoking(a => a.Parse("1 2 3")) + argument.Invoking(a => new RootCommand { a }.Parse("1 2 3")) .Should() .Throw() .Which diff --git a/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs b/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs index 419d8b2fb9..0bec7d6d2f 100644 --- a/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs +++ b/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs @@ -987,7 +987,7 @@ public void Sequence_type_defaults_to_empty_when_not_specified(Type sequenceType private void AssertParsedValueIsEmpty(Argument argument) where T : IEnumerable { - var result = argument.Parse(""); + var result = new RootCommand { argument }.Parse(""); result.GetValue(argument) .Should() diff --git a/src/System.CommandLine.Tests/ParsingValidationTests.cs b/src/System.CommandLine.Tests/ParsingValidationTests.cs index 85710b9395..90c8687df0 100644 --- a/src/System.CommandLine.Tests/ParsingValidationTests.cs +++ b/src/System.CommandLine.Tests/ParsingValidationTests.cs @@ -561,7 +561,7 @@ public void The_parsed_value_of_an_argument_is_available_within_a_validator() } }); - var result = argument.Parse("-1"); + var result = new RootCommand() { argument }.Parse("-1"); result.Errors .Should() diff --git a/src/System.CommandLine.Tests/SymbolTests.cs b/src/System.CommandLine.Tests/SymbolTests.cs index 71401618f9..728dab5982 100644 --- a/src/System.CommandLine.Tests/SymbolTests.cs +++ b/src/System.CommandLine.Tests/SymbolTests.cs @@ -18,25 +18,6 @@ public void When_Name_is_explicitly_set_then_adding_aliases_does_not_change_it() symbol.Name.Should().Be("changed"); } - [Fact] - public void Parse_extension_method_reuses_implicit_parser_instance() - { - var symbol = CreateSymbol("x"); - - Func parse = () => symbol switch - { - Argument argument => argument.Parse(""), - Command command => command.Parse(""), - Option option => option.Parse(""), - _ => throw new ArgumentOutOfRangeException(nameof(symbol)) - }; - - var parser1 = parse().Parser; - var parser2 = parse().Parser; - - parser1.Should().BeSameAs(parser2); - } - protected abstract Symbol CreateSymbol(string name); } } \ No newline at end of file diff --git a/src/System.CommandLine/Argument.cs b/src/System.CommandLine/Argument.cs index b9bf36e259..ae5276b1b3 100644 --- a/src/System.CommandLine/Argument.cs +++ b/src/System.CommandLine/Argument.cs @@ -141,22 +141,5 @@ public override IEnumerable GetCompletions(CompletionContext con /// string IValueDescriptor.ValueName => Name; - - /// - /// Parses a command line string value using the argument. - /// - /// The command line string input will be split into tokens as if it had been passed on the command line. - /// A command line string to parse, which can include spaces and quotes equivalent to what can be entered into a terminal. - /// A parse result describing the outcome of the parse operation. - public ParseResult Parse(string commandLine) => - this.GetOrCreateDefaultSimpleParser().Parse(commandLine); - - /// - /// Parses a command line string value using the argument. - /// - /// The string arguments to parse. - /// A parse result describing the outcome of the parse operation. - public ParseResult Parse(string[] args) => - this.GetOrCreateDefaultSimpleParser().Parse(args); } } From 816aa607c3f4e8d54296de25aff4ace57131fdb9 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Sat, 18 Feb 2023 21:36:40 +0100 Subject: [PATCH 02/13] remove Option.Parse as it has side effects: creating RootCommand and setting it as a Parent of given Argument --- ...ommandLine_api_is_not_changed.approved.txt | 4 - .../ModelBinderTests.cs | 2 +- src/System.CommandLine.Tests/ArgumentTests.cs | 2 +- .../Binding/TypeConversionTests.cs | 98 +++++++++---------- .../CompletionTests.cs | 2 +- .../DirectiveTests.cs | 20 ++-- .../OptionTests.MultipleArgumentsPerToken.cs | 2 +- src/System.CommandLine.Tests/OptionTests.cs | 16 +-- src/System.CommandLine.Tests/ParserTests.cs | 10 +- .../ParsingValidationTests.cs | 10 +- .../ResponseFileTests.cs | 2 +- src/System.CommandLine/Option.cs | 17 ---- 12 files changed, 82 insertions(+), 103 deletions(-) diff --git a/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt b/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt index 9a07c56d77..bb4130df8a 100644 --- a/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt +++ b/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt @@ -8,8 +8,6 @@ System.CommandLine public System.Type ValueType { get; } public System.Collections.Generic.IEnumerable GetCompletions(System.CommandLine.Completions.CompletionContext context) public System.Object GetDefaultValue() - public ParseResult Parse(System.String commandLine) - public ParseResult Parse(System.String[] args) public System.String ToString() public class Argument : Argument, IValueDescriptor, System.CommandLine.Binding.IValueDescriptor .ctor() @@ -191,8 +189,6 @@ System.CommandLine public System.Collections.Generic.List> Validators { get; } public System.Type ValueType { get; } public System.Collections.Generic.IEnumerable GetCompletions(System.CommandLine.Completions.CompletionContext context) - public ParseResult Parse(System.String commandLine) - public ParseResult Parse(System.String[] args) public class Option : Option, IValueDescriptor, System.CommandLine.Binding.IValueDescriptor .ctor(System.String name, System.String description = null) .ctor(System.String[] aliases, System.String description = null) diff --git a/src/System.CommandLine.NamingConventionBinder.Tests/ModelBinderTests.cs b/src/System.CommandLine.NamingConventionBinder.Tests/ModelBinderTests.cs index 064b93a9e4..9fef147689 100644 --- a/src/System.CommandLine.NamingConventionBinder.Tests/ModelBinderTests.cs +++ b/src/System.CommandLine.NamingConventionBinder.Tests/ModelBinderTests.cs @@ -481,7 +481,7 @@ public void Option_argument_is_bound_to_longest_constructor() { var option = new Option("--int-property"); - var bindingContext = new InvocationContext(option.Parse("--int-property 42")).BindingContext; + var bindingContext = new InvocationContext(new RootCommand { option }.Parse("--int-property 42")).BindingContext; var binder = new ModelBinder(); var instance = binder.CreateInstance(bindingContext) as ClassWithMultipleCtor; diff --git a/src/System.CommandLine.Tests/ArgumentTests.cs b/src/System.CommandLine.Tests/ArgumentTests.cs index 36de8980f9..73db8bbec5 100644 --- a/src/System.CommandLine.Tests/ArgumentTests.cs +++ b/src/System.CommandLine.Tests/ArgumentTests.cs @@ -168,7 +168,7 @@ public void Validation_failure_message_can_be_specified_when_evaluating_default_ return null; }, true); - option.Parse("") + new RootCommand { option }.Parse("") .Errors .Should() .ContainSingle() diff --git a/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs b/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs index 0bec7d6d2f..c3ce14de47 100644 --- a/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs +++ b/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs @@ -20,7 +20,7 @@ public void Option_argument_of_FileInfo_can_be_bound_without_custom_conversion_l var option = new Option("--file"); var file = new FileInfo(Path.Combine(new DirectoryInfo("temp").FullName, "the-file.txt")); - var result = option.Parse($"--file {file.FullName}"); + var result = new RootCommand { option }.Parse($"--file {file.FullName}"); result.GetValue(option) .Name @@ -70,7 +70,7 @@ public void Command_argument_of_FileInfo_returns_null_when_argument_is_not_provi public void Argument_of_FileInfo_that_is_empty_results_in_an_informative_error() { var option = new Option("--file"); - var result = option.Parse(new string[] { "--file", "" }); + var result = new RootCommand { option }.Parse(new string[] { "--file", "" }); result.Errors .Should() @@ -88,7 +88,7 @@ public void Argument_of_array_of_FileInfo_can_be_called_without_custom_conversio var file1 = new FileInfo(Path.Combine(new DirectoryInfo("temp").FullName, "file1.txt")); var file2 = new FileInfo(Path.Combine(new DirectoryInfo("temp").FullName, "file2.txt")); - var result = option.Parse($"--file {file1.FullName} --file {file2.FullName}"); + var result = new RootCommand { option }.Parse($"--file {file1.FullName} --file {file2.FullName}"); result.GetValue(option) .Select(fi => fi.Name) @@ -231,7 +231,7 @@ public void Nullable_bool_parses_as_null_when_the_option_has_not_been_applied() { var option = new Option("-x"); - option + new RootCommand { option } .Parse("") .GetValue(option) .Should() @@ -431,7 +431,7 @@ public void Values_can_be_correctly_converted_to_DateTime_without_the_parser_spe var option = new Option("-x"); var dateString = "2022-02-06T01:46:03.0000000-08:00"; - var value = option.Parse($"-x {dateString}").GetValue(option); + var value = new RootCommand { option }.Parse($"-x {dateString}").GetValue(option); value.Should().Be(DateTime.Parse(dateString)); } @@ -443,7 +443,7 @@ public void Values_can_be_correctly_converted_to_nullable_DateTime_without_the_p var option = new Option("-x"); var dateString = "2022-02-06T01:46:03.0000000-08:00"; - var value = option.Parse($"-x {dateString}").GetValue(option); + var value = new RootCommand { option }.Parse($"-x {dateString}").GetValue(option); value.Should().Be(DateTime.Parse(dateString)); } @@ -454,7 +454,7 @@ public void Values_can_be_correctly_converted_to_DateTimeOffset_without_the_pars var option = new Option("-x"); var dateString = "2022-02-06T09:52:54.5275055-08:00"; - var value = option.Parse($"-x {dateString}").GetValue(option); + var value = new RootCommand { option }.Parse($"-x {dateString}").GetValue(option); value.Should().Be(DateTime.Parse(dateString)); } @@ -465,7 +465,7 @@ public void Values_can_be_correctly_converted_to_nullable_DateTimeOffset_without var option = new Option("-x"); var dateString = "2022-02-06T09:52:54.5275055-08:00"; - var value = option.Parse($"-x {dateString}").GetValue(option); + var value = new RootCommand { option }.Parse($"-x {dateString}").GetValue(option); value.Should().Be(DateTime.Parse(dateString)); } @@ -475,7 +475,7 @@ public void Values_can_be_correctly_converted_to_decimal_without_the_parser_spec { var option = new Option("-x"); - var result = option.Parse("-x 123.456"); + var result = new RootCommand { option }.Parse("-x 123.456"); var value = result.GetValue(option); @@ -487,7 +487,7 @@ public void Values_can_be_correctly_converted_to_nullable_decimal_without_the_pa { var option = new Option("-x"); - var value = option.Parse("-x 123.456").GetValue(option); + var value = new RootCommand { option }.Parse("-x 123.456").GetValue(option); value.Should().Be(123.456m); } @@ -497,7 +497,7 @@ public void Values_can_be_correctly_converted_to_double_without_the_parser_speci { var option = new Option("-x"); - var value = option.Parse("-x 123.456").GetValue(option); + var value = new RootCommand { option }.Parse("-x 123.456").GetValue(option); value.Should().Be(123.456d); } @@ -507,7 +507,7 @@ public void Values_can_be_correctly_converted_to_nullable_double_without_the_par { var option = new Option("-x"); - var value = option.Parse("-x 123.456").GetValue(option); + var value = new RootCommand { option }.Parse("-x 123.456").GetValue(option); value.Should().Be(123.456d); } @@ -517,7 +517,7 @@ public void Values_can_be_correctly_converted_to_float_without_the_parser_specif { var option = new Option("-x"); - var value = option.Parse("-x 123.456").GetValue(option); + var value = new RootCommand { option }.Parse("-x 123.456").GetValue(option); value.Should().Be(123.456f); } @@ -527,7 +527,7 @@ public void Values_can_be_correctly_converted_to_nullable_float_without_the_pars { var option = new Option("-x"); - var value = option.Parse("-x 123.456").GetValue(option); + var value = new RootCommand { option }.Parse("-x 123.456").GetValue(option); value.Should().Be(123.456f); } @@ -538,7 +538,7 @@ public void Values_can_be_correctly_converted_to_Guid_without_the_parser_specify var guidString = "75517282-018F-46BB-B15F-1D8DBFE23F6E"; var option = new Option("-x"); - var value = option.Parse($"-x {guidString}").GetValue(option); + var value = new RootCommand { option }.Parse($"-x {guidString}").GetValue(option); value.Should().Be(Guid.Parse(guidString)); } @@ -549,7 +549,7 @@ public void Values_can_be_correctly_converted_to_nullable_Guid_without_the_parse var guidString = "75517282-018F-46BB-B15F-1D8DBFE23F6E"; var option = new Option("-x"); - var value = option.Parse($"-x {guidString}").GetValue(option); + var value = new RootCommand { option }.Parse($"-x {guidString}").GetValue(option); value.Should().Be(Guid.Parse(guidString)); } @@ -560,7 +560,7 @@ public void Values_can_be_correctly_converted_to_TimeSpan_without_the_parser_spe var timeSpanString = "30"; var option = new Option("-x"); - var value = option.Parse($"-x {timeSpanString}").GetValue(option); + var value = new RootCommand { option }.Parse($"-x {timeSpanString}").GetValue(option); value.Should().Be(TimeSpan.Parse(timeSpanString)); } @@ -571,7 +571,7 @@ public void Values_can_be_correctly_converted_to_nullable_TimeSpan_without_the_p var timeSpanString = "30"; var option = new Option("-x"); - var value = option.Parse($"-x {timeSpanString}").GetValue(option); + var value = new RootCommand { option }.Parse($"-x {timeSpanString}").GetValue(option); value.Should().Be(TimeSpan.Parse(timeSpanString)); } @@ -581,7 +581,7 @@ public void Values_can_be_correctly_converted_to_Uri_without_the_parser_specifyi { var option = new Option("-x"); - var value = option.Parse("-x http://example.com").GetValue(option); + var value = new RootCommand { option }.Parse("-x http://example.com").GetValue(option); value.Should().BeEquivalentTo(new Uri("http://example.com")); } @@ -591,8 +591,8 @@ public void Options_with_arguments_specified_can_be_correctly_converted_to_bool_ { var option = new Option("-x"); - option.Parse("-x false").GetValue(option).Should().BeFalse(); - option.Parse("-x true").GetValue(option).Should().BeTrue(); + new RootCommand { option }.Parse("-x false").GetValue(option).Should().BeFalse(); + new RootCommand { option }.Parse("-x true").GetValue(option).Should().BeTrue(); } [Fact] @@ -600,7 +600,7 @@ public void Values_can_be_correctly_converted_to_long_without_the_parser_specify { var option = new Option("-x"); - var value = option.Parse("-x 123456790").GetValue(option); + var value = new RootCommand { option }.Parse("-x 123456790").GetValue(option); value.Should().Be(123456790L); } @@ -610,7 +610,7 @@ public void Values_can_be_correctly_converted_to_nullable_long_without_the_parse { var option = new Option("-x"); - var value = option.Parse("-x 1234567890").GetValue(option); + var value = new RootCommand { option }.Parse("-x 1234567890").GetValue(option); value.Should().Be(1234567890L); } @@ -620,7 +620,7 @@ public void Values_can_be_correctly_converted_to_short_without_the_parser_specif { var option = new Option("-s"); - var value = option.Parse("-s 1234").GetValue(option); + var value = new RootCommand { option }.Parse("-s 1234").GetValue(option); value.Should().Be(1234); } @@ -630,7 +630,7 @@ public void Values_can_be_correctly_converted_to_nullable_short_without_the_pars { var option = new Option("-s"); - var value = option.Parse("-s 1234").GetValue(option); + var value = new RootCommand { option }.Parse("-s 1234").GetValue(option); value.Should().Be(1234); } @@ -640,7 +640,7 @@ public void Values_can_be_correctly_converted_to_ulong_without_the_parser_specif { var option = new Option("-x"); - var value = option.Parse("-x 1234").GetValue(option); + var value = new RootCommand { option }.Parse("-x 1234").GetValue(option); value.Should().Be(1234); } @@ -650,7 +650,7 @@ public void Values_can_be_correctly_converted_to_nullable_ulong_without_the_pars { var option = new Option("-x"); - var value = option.Parse("-x 1234").GetValue(option); + var value = new RootCommand { option }.Parse("-x 1234").GetValue(option); value.Should().Be(1234); } @@ -660,7 +660,7 @@ public void Values_can_be_correctly_converted_to_ushort_without_the_parser_speci { var option = new Option("-x"); - var value = option.Parse("-x 1234").GetValue(option); + var value = new RootCommand { option }.Parse("-x 1234").GetValue(option); value.Should().Be(1234); } @@ -670,7 +670,7 @@ public void Values_can_be_correctly_converted_to_nullable_ushort_without_the_par { var option = new Option("-x"); - var value = option.Parse("-x 1234").GetValue(option); + var value = new RootCommand { option }.Parse("-x 1234").GetValue(option); value.Should().Be(1234); } @@ -680,7 +680,7 @@ public void Values_can_be_correctly_converted_to_sbyte_without_the_parser_specif { var option = new Option("-us"); - var value = option.Parse("-us 123").GetValue(option); + var value = new RootCommand { option }.Parse("-us 123").GetValue(option); value.Should().Be(123); } @@ -690,7 +690,7 @@ public void Values_can_be_correctly_converted_to_nullable_sbyte_without_the_pars { var option = new Option("-x"); - var value = option.Parse("-x 123").GetValue(option); + var value = new RootCommand { option }.Parse("-x 123").GetValue(option); value.Should().Be(123); } @@ -700,7 +700,7 @@ public void Values_can_be_correctly_converted_to_ipaddress_without_the_parser_sp { var option = new Option("-us"); - var value = option.Parse("-us 1.2.3.4").GetValue(option); + var value = new RootCommand { option }.Parse("-us 1.2.3.4").GetValue(option); value.Should().Be(IPAddress.Parse("1.2.3.4")); } @@ -711,7 +711,7 @@ public void Values_can_be_correctly_converted_to_ipendpoint_without_the_parser_s { var option = new Option("-us"); - var value = option.Parse("-us 1.2.3.4:56").GetValue(option); + var value = new RootCommand { option }.Parse("-us 1.2.3.4:56").GetValue(option); value.Should().Be(IPEndPoint.Parse("1.2.3.4:56")); } @@ -723,7 +723,7 @@ public void Values_can_be_correctly_converted_to_dateonly_without_the_parser_spe { var option = new Option("-us"); - var value = option.Parse("-us 2022-03-02").GetValue(option); + var value = new RootCommand { option }.Parse("-us 2022-03-02").GetValue(option); value.Should().Be(DateOnly.Parse("2022-03-02")); } @@ -733,7 +733,7 @@ public void Values_can_be_correctly_converted_to_nullable_dateonly_without_the_p { var option = new Option("-x"); - var value = option.Parse("-x 2022-03-02").GetValue(option); + var value = new RootCommand { option }.Parse("-x 2022-03-02").GetValue(option); value.Should().Be(DateOnly.Parse("2022-03-02")); } @@ -743,7 +743,7 @@ public void Values_can_be_correctly_converted_to_timeonly_without_the_parser_spe { var option = new Option("-us"); - var value = option.Parse("-us 12:34:56").GetValue(option); + var value = new RootCommand { option }.Parse("-us 12:34:56").GetValue(option); value.Should().Be(TimeOnly.Parse("12:34:56")); } @@ -753,7 +753,7 @@ public void Values_can_be_correctly_converted_to_nullable_timeonly_without_the_p { var option = new Option("-x"); - var value = option.Parse("-x 12:34:56").GetValue(option); + var value = new RootCommand { option }.Parse("-x 12:34:56").GetValue(option); value.Should().Be(TimeOnly.Parse("12:34:56")); } @@ -764,7 +764,7 @@ public void Values_can_be_correctly_converted_to_byte_without_the_parser_specify { var option = new Option("-us"); - var value = option.Parse("-us 123").GetValue(option); + var value = new RootCommand { option }.Parse("-us 123").GetValue(option); value.Should().Be(123); } @@ -774,7 +774,7 @@ public void Values_can_be_correctly_converted_to_nullable_byte_without_the_parse { var option = new Option("-x"); - var value = option.Parse("-x 123").GetValue(option); + var value = new RootCommand { option }.Parse("-x 123").GetValue(option); value.Should().Be(123); } @@ -784,7 +784,7 @@ public void Values_can_be_correctly_converted_to_uint_without_the_parser_specify { var option = new Option("-us"); - var value = option.Parse("-us 1234").GetValue(option); + var value = new RootCommand { option }.Parse("-us 1234").GetValue(option); value.Should().Be(1234); } @@ -794,7 +794,7 @@ public void Values_can_be_correctly_converted_to_nullable_uint_without_the_parse { var option = new Option("-x"); - var value = option.Parse("-x 1234").GetValue(option); + var value = new RootCommand { option }.Parse("-x 1234").GetValue(option); value.Should().Be(1234); } @@ -804,7 +804,7 @@ public void Values_can_be_correctly_converted_to_array_of_int_without_the_parser { var option = new Option("-x"); - var value = option.Parse("-x 1 -x 2 -x 3").GetValue(option); + var value = new RootCommand { option }.Parse("-x 1 -x 2 -x 3").GetValue(option); value.Should().BeEquivalentTo(1, 2, 3); } @@ -855,7 +855,7 @@ public void Values_can_be_correctly_converted_to_List_of_int_without_the_parser_ { var option = new Option>("-x"); - var value = option.Parse("-x 1 -x 2 -x 3").GetValue(option); + var value = new RootCommand { option }.Parse("-x 1 -x 2 -x 3").GetValue(option); value.Should().BeEquivalentTo(1, 2, 3); } @@ -865,7 +865,7 @@ public void Values_can_be_correctly_converted_to_IEnumerable_of_int_without_the_ { var option = new Option>("-x"); - var value = option.Parse("-x 1 -x 2 -x 3").GetValue(option); + var value = new RootCommand { option }.Parse("-x 1 -x 2 -x 3").GetValue(option); value.Should().BeEquivalentTo(1, 2, 3); } @@ -875,7 +875,7 @@ public void Enum_values_can_be_correctly_converted_based_on_enum_value_name_with { var option = new Option("-x"); - var parseResult = option.Parse("-x Monday"); + var parseResult = new RootCommand { option }.Parse("-x Monday"); var value = parseResult.GetValue(option); @@ -887,7 +887,7 @@ public void Nullable_enum_values_can_be_correctly_converted_based_on_enum_value_ { var option = new Option("-x"); - var parseResult = option.Parse("-x Monday"); + var parseResult = new RootCommand { option }.Parse("-x Monday"); var value = parseResult.GetValue(option); @@ -899,7 +899,7 @@ public void Enum_values_that_cannot_be_parsed_result_in_an_informative_error() { var option = new Option("-x"); - var value = option.Parse("-x Notaday"); + var value = new RootCommand { option }.Parse("-x Notaday"); value.Errors .Should() @@ -915,7 +915,7 @@ public void When_getting_a_single_value_and_specifying_a_conversion_type_that_is { var option = new Option("-x"); - var result = option.Parse("-x not-an-int"); + var result = new RootCommand { option }.Parse("-x not-an-int"); Action getValue = () => result.GetValue(option); @@ -932,7 +932,7 @@ public void When_getting_an_array_of_values_and_specifying_a_conversion_type_tha { var option = new Option("-x"); - var result = option.Parse("-x not-an-int -x 2"); + var result = new RootCommand { option }.Parse("-x not-an-int -x 2"); Action getValue = () => result.GetValue(option); diff --git a/src/System.CommandLine.Tests/CompletionTests.cs b/src/System.CommandLine.Tests/CompletionTests.cs index 29f9d2aaae..486c216f01 100644 --- a/src/System.CommandLine.Tests/CompletionTests.cs +++ b/src/System.CommandLine.Tests/CompletionTests.cs @@ -956,7 +956,7 @@ public void When_option_completions_are_available_then_they_are_suggested_when_a { var option = new Option("--day"); - var result = option.Parse("--day SleepyDay"); + var result = new RootCommand { option }.Parse("--day SleepyDay"); result.Errors .Should() diff --git a/src/System.CommandLine.Tests/DirectiveTests.cs b/src/System.CommandLine.Tests/DirectiveTests.cs index 0a6b5e8bf5..a8ec625078 100644 --- a/src/System.CommandLine.Tests/DirectiveTests.cs +++ b/src/System.CommandLine.Tests/DirectiveTests.cs @@ -16,7 +16,7 @@ public void Directives_should_not_be_considered_as_unmatched_tokens() { var option = new Option("-y"); - var result = option.Parse($"{RootCommand.ExecutableName} [parse] -y"); + var result = new RootCommand { option }.Parse($"{RootCommand.ExecutableName} [parse] -y"); result.UnmatchedTokens.Should().BeEmpty(); } @@ -26,7 +26,7 @@ public void Raw_tokens_still_hold_directives() { var option = new Option("-y"); - var result = option.Parse("[parse] -y"); + var result = new RootCommand { option }.Parse("[parse] -y"); result.Directives.ContainsKey("parse").Should().BeTrue(); result.Tokens.Should().Contain(t => t.Value == "[parse]"); @@ -37,7 +37,7 @@ public void Directives_should_parse_into_the_directives_collection() { var option = new Option("-y"); - var result = option.Parse("[parse] -y"); + var result = new RootCommand { option }.Parse("[parse] -y"); result.Directives.ContainsKey("parse").Should().BeTrue(); } @@ -47,7 +47,7 @@ public void Multiple_directives_are_allowed() { var option = new Option("-y"); - var result = option.Parse("[parse] [suggest] -y"); + var result = new RootCommand { option }.Parse("[parse] [suggest] -y"); result.Directives.ContainsKey("parse").Should().BeTrue(); result.Directives.ContainsKey("suggest").Should().BeTrue(); @@ -58,7 +58,7 @@ public void Directives_must_be_the_first_argument() { var option = new Option("-y"); - var result = option.Parse("-y [suggest]"); + var result = new RootCommand { option }.Parse("-y [suggest]"); result.Directives.Should().BeEmpty(); } @@ -74,7 +74,7 @@ public void Directives_can_have_a_value_which_is_everything_after_the_first_colo { var option = new Option("-y"); - var result = option.Parse($"{directive} -y"); + var result = new RootCommand { option }.Parse($"{directive} -y"); result.Directives.TryGetValue(expectedKey, out var values).Should().BeTrue(); values.Should().BeEquivalentTo(expectedValue); @@ -85,7 +85,7 @@ public void Directives_without_a_value_specified_have_a_value_of_empty_string() { var option = new Option("-y"); - var result = option.Parse("[parse] -y"); + var result = new RootCommand { option }.Parse("[parse] -y"); result.Directives.TryGetValue("parse", out var values).Should().BeTrue(); values.Should().BeEmpty(); @@ -98,7 +98,7 @@ public void Directives_must_have_a_non_empty_key(string directive) { var option = new Option("-a"); - var result = option.Parse($"{directive} -a"); + var result = new RootCommand { option }.Parse($"{directive} -a"); result.Directives.Should().BeEmpty(); result.UnmatchedTokens.Should().Contain(directive); @@ -112,7 +112,7 @@ public void Directives_cannot_contain_spaces(object value) { var option = new Option("-a"); - var result = option.Parse($"{value} -a"); + var result = new RootCommand { option }.Parse($"{value} -a"); result.Directives.Should().BeEmpty(); } @@ -122,7 +122,7 @@ public void When_a_directive_is_specified_more_than_once_then_its_values_are_agg { var option = new Option("-a"); - var result = option.Parse("[directive:one] [directive:two] -a"); + var result = new RootCommand { option }.Parse("[directive:one] [directive:two] -a"); result.Directives.TryGetValue("directive", out var values).Should().BeTrue(); values.Should().BeEquivalentTo("one", "two"); diff --git a/src/System.CommandLine.Tests/OptionTests.MultipleArgumentsPerToken.cs b/src/System.CommandLine.Tests/OptionTests.MultipleArgumentsPerToken.cs index 881975de3f..e884da159d 100644 --- a/src/System.CommandLine.Tests/OptionTests.MultipleArgumentsPerToken.cs +++ b/src/System.CommandLine.Tests/OptionTests.MultipleArgumentsPerToken.cs @@ -127,7 +127,7 @@ public void All_consumed_tokens_are_present_in_option_result() AllowMultipleArgumentsPerToken = true }; - var result = option.Parse("-x 1 -x 2 -x 3 -x 4"); + var result = new RootCommand { option }.Parse("-x 1 -x 2 -x 3 -x 4"); _output.WriteLine(result.Diagram()); diff --git a/src/System.CommandLine.Tests/OptionTests.cs b/src/System.CommandLine.Tests/OptionTests.cs index f87e74c5c0..7a56c57d51 100644 --- a/src/System.CommandLine.Tests/OptionTests.cs +++ b/src/System.CommandLine.Tests/OptionTests.cs @@ -244,7 +244,7 @@ public void Option_T_default_value_can_be_set_via_the_constructor() parseArgument: parsed => 123, isDefault: true); - option + new RootCommand { option } .Parse("") .FindResultFor(option) .GetValueOrDefault() @@ -259,7 +259,7 @@ public void Option_T_default_value_can_be_set_after_instantiation() option.SetDefaultValue(123); - option + new RootCommand { option } .Parse("") .FindResultFor(option) .GetValueOrDefault() @@ -274,7 +274,7 @@ public void Option_T_default_value_factory_can_be_set_after_instantiation() option.SetDefaultValueFactory(() => 123); - option + new RootCommand { option } .Parse("") .FindResultFor(option) .GetValueOrDefault() @@ -293,7 +293,7 @@ public void Option_T_default_value_is_validated() .Select(_ => "ERR") .First())); - option + new RootCommand { option } .Parse("-x 123") .Errors .Select(e => e.Message) @@ -306,7 +306,7 @@ public void Option_of_string_defaults_to_null_when_not_specified() { var option = new Option("-x"); - var result = option.Parse(""); + var result = new RootCommand { option }.Parse(""); result.HasOption(option) .Should() .BeFalse(); @@ -335,7 +335,7 @@ public void When_aliases_overlap_the_longer_alias_is_chosen(string parseInput) { var option = new Option(new[] { "-o", "-option" }); - var parseResult = option.Parse(parseInput); + var parseResult = new RootCommand { option }.Parse(parseInput); parseResult.GetValue(option).Should().Be("value"); } @@ -345,7 +345,7 @@ public void Option_of_boolean_defaults_to_false_when_not_specified() { var option = new Option("-x"); - var result = option.Parse(""); + var result = new RootCommand { option }.Parse(""); result.HasOption(option) .Should() @@ -361,7 +361,7 @@ public void Option_of_enum_can_limit_enum_members_as_valid_values() Option option = new("--color"); option.AcceptOnlyFromAmong(ConsoleColor.Red.ToString(), ConsoleColor.Green.ToString()); - var result = option.Parse("--color Fuschia"); + var result = new RootCommand { option }.Parse("--color Fuschia"); result.Errors .Select(e => e.Message) diff --git a/src/System.CommandLine.Tests/ParserTests.cs b/src/System.CommandLine.Tests/ParserTests.cs index 127f228cdf..604b5b8ec2 100644 --- a/src/System.CommandLine.Tests/ParserTests.cs +++ b/src/System.CommandLine.Tests/ParserTests.cs @@ -74,7 +74,7 @@ public void Short_form_options_can_be_specified_using_equals_delimiter() { var option = new Option("-x"); - var result = option.Parse("-x=some-value"); + var result = new RootCommand { option }.Parse("-x=some-value"); result.Errors.Should().BeEmpty(); @@ -86,7 +86,7 @@ public void Long_form_options_can_be_specified_using_equals_delimiter() { var option = new Option("--hello"); - var result = option.Parse("--hello=there"); + var result = new RootCommand { option }.Parse("--hello=there"); result.Errors.Should().BeEmpty(); @@ -98,7 +98,7 @@ public void Short_form_options_can_be_specified_using_colon_delimiter() { var option = new Option("-x"); - var result = option.Parse("-x:some-value"); + var result = new RootCommand { option }.Parse("-x:some-value"); result.Errors.Should().BeEmpty(); @@ -110,7 +110,7 @@ public void Long_form_options_can_be_specified_using_colon_delimiter() { var option = new Option("--hello"); - var result = option.Parse("--hello:there"); + var result = new RootCommand { option }.Parse("--hello:there"); result.Errors.Should().BeEmpty(); @@ -1052,7 +1052,7 @@ public void When_an_option_argument_is_enclosed_in_double_quotes_its_value_retai { var option = new Option("-x"); - var parseResult = option.Parse(new[] { arg1, arg2 }); + var parseResult = new RootCommand { option }.Parse(new[] { arg1, arg2 }); parseResult .FindResultFor(option) diff --git a/src/System.CommandLine.Tests/ParsingValidationTests.cs b/src/System.CommandLine.Tests/ParsingValidationTests.cs index 90c8687df0..40a5c852cd 100644 --- a/src/System.CommandLine.Tests/ParsingValidationTests.cs +++ b/src/System.CommandLine.Tests/ParsingValidationTests.cs @@ -27,7 +27,7 @@ public void When_an_option_accepts_only_specific_arguments_but_a_wrong_one_is_su var option = new Option("-x"); option.AcceptOnlyFromAmong("this", "that", "the-other-thing"); - var result = option.Parse("-x none-of-those"); + var result = new RootCommand { option }.Parse("-x none-of-those"); result.Errors .Select(e => e.Message) @@ -43,7 +43,7 @@ public void When_an_option_has_en_error_then_the_error_has_a_reference_to_the_op var option = new Option("-x"); option.AcceptOnlyFromAmong("this", "that"); - var result = option.Parse("-x something_else"); + var result = new RootCommand { option }.Parse("-x something_else"); result.Errors .Where(e => e.SymbolResult != null) @@ -210,7 +210,7 @@ public void When_a_required_argument_is_not_supplied_then_an_error_is_returned() { var option = new Option("-x"); - var result = option.Parse("-x"); + var result = new RootCommand { option }.Parse("-x"); result.Errors .Should() @@ -585,7 +585,7 @@ public void The_parsed_value_of_an_option_is_available_within_a_validator() } }); - var result = option.Parse("-x -1"); + var result = new RootCommand { option }.Parse("-x -1"); result.Errors .Should() @@ -1162,7 +1162,7 @@ public void When_an_option_has_a_default_value_it_is_not_valid_to_specify_the_op { var option = new Option("-x", () => 123); - var result = option.Parse("-x"); + var result = new RootCommand { option }.Parse("-x"); result.Errors .Select(e => e.Message) diff --git a/src/System.CommandLine.Tests/ResponseFileTests.cs b/src/System.CommandLine.Tests/ResponseFileTests.cs index d50eda4c20..25db892d24 100644 --- a/src/System.CommandLine.Tests/ResponseFileTests.cs +++ b/src/System.CommandLine.Tests/ResponseFileTests.cs @@ -45,7 +45,7 @@ public void When_response_file_specified_it_loads_options_from_response_file() { var option = new Option("--flag"); - var result = option.Parse($"@{CreateResponseFile("--flag")}"); + var result = new RootCommand { option }.Parse($"@{CreateResponseFile("--flag")}"); result.HasOption(option).Should().BeTrue(); } diff --git a/src/System.CommandLine/Option.cs b/src/System.CommandLine/Option.cs index f1a473de42..87292dee05 100644 --- a/src/System.CommandLine/Option.cs +++ b/src/System.CommandLine/Option.cs @@ -153,22 +153,5 @@ public override IEnumerable GetCompletions(CompletionContext con .OrderBy(item => item.SortText.IndexOfCaseInsensitive(context.WordToComplete)) .ThenBy(symbol => symbol.Label, StringComparer.OrdinalIgnoreCase); } - - /// - /// Parses a command line string value using the option. - /// - /// The command line string input will be split into tokens as if it had been passed on the command line. - /// A command line string to parse, which can include spaces and quotes equivalent to what can be entered into a terminal. - /// A parse result describing the outcome of the parse operation. - public ParseResult Parse(string commandLine) => - this.GetOrCreateDefaultSimpleParser().Parse(commandLine); - - /// - /// Parses a command line string value using the option. - /// - /// The string options to parse. - /// A parse result describing the outcome of the parse operation. - public ParseResult Parse(string[] args) => - this.GetOrCreateDefaultSimpleParser().Parse(args); } } From c904e8f5c21ac5da4817108bfacb6879f830cdd4 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Sat, 18 Feb 2023 21:40:25 +0100 Subject: [PATCH 03/13] Move CommandExtensions.Parse to Command.Parse to make it easier to discover --- ...ommandLine_api_is_not_changed.approved.txt | 4 ++-- src/System.CommandLine/Command.cs | 17 ++++++++++++++ src/System.CommandLine/CommandExtensions.cs | 23 ------------------- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt b/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt index bb4130df8a..560f5953d6 100644 --- a/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt +++ b/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt @@ -54,13 +54,13 @@ System.CommandLine public System.Void Add(Symbol symbol) public System.Collections.Generic.IEnumerable GetCompletions(System.CommandLine.Completions.CompletionContext context) public System.Collections.Generic.IEnumerator GetEnumerator() + public ParseResult Parse(System.String[] args) + public ParseResult Parse(System.String commandLine) public static class CommandExtensions public static System.Int32 Invoke(this Command command, System.String[] args, IConsole console = null) public static System.Int32 Invoke(this Command command, System.String commandLine, IConsole console = null) public static System.Threading.Tasks.Task InvokeAsync(this Command command, System.String[] args, IConsole console = null, System.Threading.CancellationToken cancellationToken = null) public static System.Threading.Tasks.Task InvokeAsync(this Command command, System.String commandLine, IConsole console = null, System.Threading.CancellationToken cancellationToken = null) - public static ParseResult Parse(this Command command, System.String[] args) - public static ParseResult Parse(this Command command, System.String commandLine) public class CommandLineBuilder .ctor(Command rootCommand = null) public Command Command { get; } diff --git a/src/System.CommandLine/Command.cs b/src/System.CommandLine/Command.cs index 7bd9f12a43..e7ad334625 100644 --- a/src/System.CommandLine/Command.cs +++ b/src/System.CommandLine/Command.cs @@ -136,6 +136,23 @@ public void Add(Symbol symbol) internal Parser? ImplicitSimpleParser { get; set; } + /// + /// Parses an array strings using the command. + /// + /// The string arguments to parse. + /// A parse result describing the outcome of the parse operation. + public ParseResult Parse(params string[] args) => + this.GetOrCreateDefaultSimpleParser().Parse(args); + + /// + /// Parses a command line string value using the command. + /// + /// The command line string input will be split into tokens as if it had been passed on the command line. + /// A command line string to parse, which can include spaces and quotes equivalent to what can be entered into a terminal. + /// A parse result describing the outcome of the parse operation. + public ParseResult Parse(string commandLine) + => this.GetOrCreateDefaultSimpleParser().Parse(commandLine); + /// public override IEnumerable GetCompletions(CompletionContext context) { diff --git a/src/System.CommandLine/CommandExtensions.cs b/src/System.CommandLine/CommandExtensions.cs index b0ecd0af40..119ca7d786 100644 --- a/src/System.CommandLine/CommandExtensions.cs +++ b/src/System.CommandLine/CommandExtensions.cs @@ -79,28 +79,5 @@ public static Task InvokeAsync( IConsole? console = null, CancellationToken cancellationToken = default) => command.InvokeAsync(CommandLineStringSplitter.Instance.Split(commandLine).ToArray(), console, cancellationToken); - - /// - /// Parses an array strings using the specified command. - /// - /// The command to use to parse the command line input. - /// The string arguments to parse. - /// A parse result describing the outcome of the parse operation. - public static ParseResult Parse( - this Command command, - params string[] args) => - command.GetOrCreateDefaultSimpleParser().Parse(args); - - /// - /// Parses a command line string value using the specified command. - /// - /// The command line string input will be split into tokens as if it had been passed on the command line. - /// The command to use to parse the command line input. - /// A command line string to parse, which can include spaces and quotes equivalent to what can be entered into a terminal. - /// A parse result describing the outcome of the parse operation. - public static ParseResult Parse( - this Command command, - string commandLine) => - command.GetOrCreateDefaultSimpleParser().Parse(commandLine); } } \ No newline at end of file From a2ffb8e8495ba53efc551462b85d2e725f2de458 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Sat, 18 Feb 2023 21:43:45 +0100 Subject: [PATCH 04/13] don't store a reference to Parser --- src/System.CommandLine/Command.cs | 7 +-- src/System.CommandLine/CommandExtensions.cs | 4 +- src/System.CommandLine/SymbolExtensions.cs | 58 --------------------- 3 files changed, 4 insertions(+), 65 deletions(-) diff --git a/src/System.CommandLine/Command.cs b/src/System.CommandLine/Command.cs index e7ad334625..1fb17fd1b8 100644 --- a/src/System.CommandLine/Command.cs +++ b/src/System.CommandLine/Command.cs @@ -132,9 +132,6 @@ public void Add(Symbol symbol) /// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - internal Parser? ImplicitInvocationParser { get; set; } - - internal Parser? ImplicitSimpleParser { get; set; } /// /// Parses an array strings using the command. @@ -142,7 +139,7 @@ public void Add(Symbol symbol) /// The string arguments to parse. /// A parse result describing the outcome of the parse operation. public ParseResult Parse(params string[] args) => - this.GetOrCreateDefaultSimpleParser().Parse(args); + new Parser(this).Parse(args); /// /// Parses a command line string value using the command. @@ -151,7 +148,7 @@ public ParseResult Parse(params string[] args) => /// A command line string to parse, which can include spaces and quotes equivalent to what can be entered into a terminal. /// A parse result describing the outcome of the parse operation. public ParseResult Parse(string commandLine) - => this.GetOrCreateDefaultSimpleParser().Parse(commandLine); + => new Parser(this).Parse(commandLine); /// public override IEnumerable GetCompletions(CompletionContext context) diff --git a/src/System.CommandLine/CommandExtensions.cs b/src/System.CommandLine/CommandExtensions.cs index 119ca7d786..e1ade4092e 100644 --- a/src/System.CommandLine/CommandExtensions.cs +++ b/src/System.CommandLine/CommandExtensions.cs @@ -26,7 +26,7 @@ public static int Invoke( string[] args, IConsole? console = null) { - ParseResult parseResult = command.GetOrCreateDefaultInvocationParser().Parse(args); + ParseResult parseResult = command.Parse(args); return InvocationPipeline.Invoke(parseResult, console); } @@ -59,7 +59,7 @@ public static Task InvokeAsync( IConsole? console = null, CancellationToken cancellationToken = default) { - ParseResult parseResult = command.GetOrCreateDefaultInvocationParser().Parse(args); + ParseResult parseResult = command.Parse(args); return InvocationPipeline.InvokeAsync(parseResult, console, cancellationToken); } diff --git a/src/System.CommandLine/SymbolExtensions.cs b/src/System.CommandLine/SymbolExtensions.cs index 66c73f1157..875ebb437d 100644 --- a/src/System.CommandLine/SymbolExtensions.cs +++ b/src/System.CommandLine/SymbolExtensions.cs @@ -31,63 +31,5 @@ internal static IList Arguments(this Symbol symbol) throw new NotSupportedException(); } } - - internal static Parser GetOrCreateDefaultSimpleParser(this Symbol symbol) - { - var root = GetOrCreateRootCommand(symbol); - - if (root.ImplicitSimpleParser is not { } parser) - { - parser = new Parser(new CommandLineConfiguration(root)); - root.ImplicitSimpleParser = parser; - } - - return parser; - } - - internal static Parser GetOrCreateDefaultInvocationParser(this Symbol symbol) - { - var root = GetOrCreateRootCommand(symbol); - - if (root.ImplicitInvocationParser is not { } parser) - { - parser = new CommandLineBuilder(root).UseDefaults().Build(); - root.ImplicitInvocationParser = parser; - } - - return parser; - } - - internal static Command GetOrCreateRootCommand(Symbol symbol) - { - if (symbol is Command cmd) - { - return cmd; - } - - if (symbol.FirstParent is null) - { - return Create(symbol); - } - - ParentNode? current = symbol.FirstParent; - while (current is not null) - { - if (current.Symbol is RootCommand root) - { - return root; - } - - current = current.Next; - } - - return Create(symbol); - - static RootCommand Create(Symbol notCommand) - => notCommand is Option option - ? new RootCommand { option } - // we know it's not a Command and not an Option, so it can only be an Argument - : new RootCommand { (Argument)notCommand }; - } } } \ No newline at end of file From 11d70c6656b292baa580e71a0e80eeecc695e726 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 20 Feb 2023 10:54:01 +0100 Subject: [PATCH 05/13] rename parser local variable to config in all tests as it's not a parser instance anymore --- ...ommandLine_api_is_not_changed.approved.txt | 31 ++-- .../Perf_Parser_CustomScenarios.cs | 11 +- .../Perf_Parser_Directives_Suggest.cs | 6 +- .../CommandLine/Perf_Parser_NestedCommands.cs | 17 +- .../CommandLine/Perf_Parser_Options_Bare.cs | 10 +- .../Perf_Parser_Options_With_Arguments.cs | 6 +- .../CommandLine/Perf_Parser_ParseResult.cs | 8 +- .../CommandLine/Perf_Parser_TypoCorrection.cs | 6 +- .../Helpers/Utils.cs | 4 +- .../ConfigureFromMethodTests.cs | 40 ++--- .../CommandLine.cs | 14 +- .../HostingHandlerTest.cs | 24 +-- .../HostingTests.cs | 48 ++--- .../ModelBinderTests.cs | 50 +++--- .../ParameterBindingTests.cs | 4 +- .../TerminalModeTests.cs | 4 +- .../ViewRenderingTests.cs | 4 +- .../SuggestionShellScriptHandlerTest.cs | 12 +- .../SuggestionDispatcher.cs | 7 +- src/System.CommandLine.Tests/ArgumentTests.cs | 3 +- .../Binding/SetHandlerTests.cs | 5 +- .../CommandExtensionsTests.cs | 34 ---- src/System.CommandLine.Tests/CommandTests.cs | 19 +- .../CompletionContextTests.cs | 8 +- .../CompletionTests.cs | 169 ++++++++++-------- .../DirectiveTests.cs | 15 +- .../EnvironmentVariableDirectiveTests.cs | 28 +-- .../Help/HelpBuilderTests.Customization.cs | 20 +-- .../Help/HelpBuilderTests.cs | 4 +- .../Invocation/InvocationPipelineTests.cs | 56 +++--- .../Invocation/TypoCorrectionTests.cs | 118 ++++++------ .../ParseDiagramTests.cs | 6 +- .../ParseDirectiveTests.cs | 12 +- .../ParseResultTests.cs | 6 +- .../ParserTests.DoubleDash.cs | 23 ++- .../ParserTests.MultipleArguments.cs | 2 +- src/System.CommandLine.Tests/ParserTests.cs | 100 +++++------ .../ParsingValidationTests.cs | 6 +- .../ResourceLocalizationTests.cs | 15 +- .../ResponseFileTests.cs | 8 +- .../SuggestDirectiveTests.cs | 93 +++++----- .../TokenReplacementTests.cs | 36 ++-- .../UseExceptionHandlerTests.cs | 12 +- src/System.CommandLine.Tests/UseHelpTests.cs | 85 ++++----- .../UseParseErrorReportingTests.cs | 10 +- .../VersionOptionTests.cs | 42 ++--- .../Builder/CommandLineBuilder.cs | 43 +++-- .../Builder/CommandLineBuilderExtensions.cs | 36 +++- src/System.CommandLine/Command.cs | 11 +- .../CommandLineConfiguration.cs | 2 + .../Invocation/InvocationContext.cs | 9 +- .../Invocation/InvocationPipeline.cs | 20 +-- .../Invocation/ParseDirectiveResult.cs | 2 +- .../Invocation/ParseErrorResult.cs | 2 +- .../Invocation/ServiceProvider.cs | 2 +- .../Invocation/SuggestDirectiveResult.cs | 2 +- .../Invocation/TypoCorrection.cs | 2 +- src/System.CommandLine/ParseResult.cs | 8 +- .../Parsing/ParseOperation.cs | 4 +- src/System.CommandLine/Parsing/Parser.cs | 54 +++--- .../Parsing/ParserExtensions.cs | 34 +--- 61 files changed, 712 insertions(+), 760 deletions(-) diff --git a/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt b/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt index 560f5953d6..7064310965 100644 --- a/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt +++ b/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt @@ -54,17 +54,17 @@ System.CommandLine public System.Void Add(Symbol symbol) public System.Collections.Generic.IEnumerable GetCompletions(System.CommandLine.Completions.CompletionContext context) public System.Collections.Generic.IEnumerator GetEnumerator() - public ParseResult Parse(System.String[] args) - public ParseResult Parse(System.String commandLine) + public ParseResult Parse(System.String[] args, CommandLineConfiguration configuration = null) + public ParseResult Parse(System.String commandLine, CommandLineConfiguration configuration = null) public static class CommandExtensions public static System.Int32 Invoke(this Command command, System.String[] args, IConsole console = null) public static System.Int32 Invoke(this Command command, System.String commandLine, IConsole console = null) public static System.Threading.Tasks.Task InvokeAsync(this Command command, System.String[] args, IConsole console = null, System.Threading.CancellationToken cancellationToken = null) public static System.Threading.Tasks.Task InvokeAsync(this Command command, System.String commandLine, IConsole console = null, System.Threading.CancellationToken cancellationToken = null) public class CommandLineBuilder - .ctor(Command rootCommand = null) + .ctor(Command rootCommand) public Command Command { get; } - public System.CommandLine.Parsing.Parser Build() + public CommandLineConfiguration Build() public static class CommandLineBuilderExtensions public static CommandLineBuilder AddMiddleware(this CommandLineBuilder builder, System.CommandLine.Invocation.InvocationMiddleware middleware, System.CommandLine.Invocation.MiddlewareOrder order = Default) public static CommandLineBuilder AddMiddleware(this CommandLineBuilder builder, System.Action onInvoke, System.CommandLine.Invocation.MiddlewareOrder order = Default) @@ -88,6 +88,7 @@ System.CommandLine public static CommandLineBuilder UseVersionOption(this CommandLineBuilder builder) public static CommandLineBuilder UseVersionOption(this CommandLineBuilder builder, System.String[] aliases) public class CommandLineConfiguration + public static CommandLineBuilder CreateBuilder(Command rootCommand) .ctor(Command command, System.Boolean enablePosixBundling = True, System.Boolean enableDirectives = True, System.Boolean enableTokenReplacement = True, LocalizationResources resources = null, System.Collections.Generic.IReadOnlyList middlewarePipeline = null, System.Func helpBuilderFactory = null, System.CommandLine.Parsing.TryReplaceToken tokenReplacer = null) public System.Boolean EnableDirectives { get; } public System.Boolean EnablePosixBundling { get; } @@ -208,9 +209,9 @@ System.CommandLine public static Option AcceptExistingOnly(this Option option) public class ParseResult public System.CommandLine.Parsing.CommandResult CommandResult { get; } + public CommandLineConfiguration Configuration { get; } public System.Collections.Generic.IReadOnlyDictionary> Directives { get; } public System.Collections.Generic.IReadOnlyList Errors { get; } - public System.CommandLine.Parsing.Parser Parser { get; } public System.CommandLine.Parsing.CommandResult RootCommandResult { get; } public System.Collections.Generic.IReadOnlyList Tokens { get; } public System.Collections.Generic.IReadOnlyList UnmatchedTokens { get; } @@ -326,7 +327,6 @@ System.CommandLine.Invocation public System.CommandLine.Help.HelpBuilder HelpBuilder { get; } public System.Action InvocationResult { get; set; } public System.CommandLine.LocalizationResources LocalizationResources { get; } - public System.CommandLine.Parsing.Parser Parser { get; } public System.CommandLine.ParseResult ParseResult { get; set; } public T GetValue(Option option) public T GetValue(Argument argument) @@ -383,6 +383,11 @@ System.CommandLine.Parsing public T GetValueOrDefault() public System.Void OnlyTake(System.Int32 numberOfTokens) public System.String ToString() + public static class CommandLineConfigurationExtensions + public static System.Int32 Invoke(this System.CommandLine.CommandLineConfiguration configuration, System.String commandLine, System.CommandLine.IConsole console = null) + public static System.Int32 Invoke(this System.CommandLine.CommandLineConfiguration configuration, System.String[] args, System.CommandLine.IConsole console = null) + public static System.Threading.Tasks.Task InvokeAsync(this System.CommandLine.CommandLineConfiguration configuration, System.String commandLine, System.CommandLine.IConsole console = null, System.Threading.CancellationToken cancellationToken = null) + public static System.Threading.Tasks.Task InvokeAsync(this System.CommandLine.CommandLineConfiguration configuration, System.String[] args, System.CommandLine.IConsole console = null, System.Threading.CancellationToken cancellationToken = null) public class CommandLineStringSplitter public System.Collections.Generic.IEnumerable Split(System.String commandLine) public class CommandResult : SymbolResult @@ -399,22 +404,14 @@ System.CommandLine.Parsing public System.String Message { get; } public SymbolResult SymbolResult { get; } public System.String ToString() - public class Parser - .ctor(System.CommandLine.CommandLineConfiguration configuration) - .ctor(System.CommandLine.Command command) - public System.CommandLine.CommandLineConfiguration Configuration { get; } - public System.CommandLine.ParseResult Parse(System.Collections.Generic.IReadOnlyList arguments, System.String rawInput = null) + public static class Parser + public static System.CommandLine.ParseResult Parse(System.CommandLine.Command command, System.Collections.Generic.IReadOnlyList arguments, System.CommandLine.CommandLineConfiguration configuration = null) + public static System.CommandLine.ParseResult Parse(System.CommandLine.Command command, System.String commandLine, System.CommandLine.CommandLineConfiguration configuration = null) public static class ParseResultExtensions public static System.String Diagram(this System.CommandLine.ParseResult parseResult) public static System.Boolean HasOption(this System.CommandLine.ParseResult parseResult, System.CommandLine.Option option) public static System.Int32 Invoke(this System.CommandLine.ParseResult parseResult, System.CommandLine.IConsole console = null) public static System.Threading.Tasks.Task InvokeAsync(this System.CommandLine.ParseResult parseResult, System.CommandLine.IConsole console = null, System.Threading.CancellationToken cancellationToken = null) - public static class ParserExtensions - public static System.Int32 Invoke(this Parser parser, System.String commandLine, System.CommandLine.IConsole console = null) - public static System.Int32 Invoke(this Parser parser, System.String[] args, System.CommandLine.IConsole console = null) - public static System.Threading.Tasks.Task InvokeAsync(this Parser parser, System.String commandLine, System.CommandLine.IConsole console = null, System.Threading.CancellationToken cancellationToken = null) - public static System.Threading.Tasks.Task InvokeAsync(this Parser parser, System.String[] args, System.CommandLine.IConsole console = null, System.Threading.CancellationToken cancellationToken = null) - public static System.CommandLine.ParseResult Parse(this Parser parser, System.String commandLine) public abstract class SymbolResult public System.CommandLine.LocalizationResources LocalizationResources { get; } public SymbolResult Parent { get; } diff --git a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_CustomScenarios.cs b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_CustomScenarios.cs index 4fa64d7cea..dce90160e9 100644 --- a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_CustomScenarios.cs +++ b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_CustomScenarios.cs @@ -13,23 +13,24 @@ namespace System.CommandLine.Benchmarks.CommandLine public class Perf_Parser_CustomScenarios { private string _testSymbolsAsString; - private Parser _testParser; + private Command _rootCommand; + private CommandLineConfiguration _configuration; [GlobalSetup(Target = nameof(OneOptWithNestedCommand_Parse))] public void SetupOneOptWithNestedCommand() { - var rootCommand = new Command("root_command"); + _rootCommand = new Command("root_command"); var nestedCommand = new Command("nested_command"); var option = new Option("-opt1", () => 123); nestedCommand.Options.Add(option); - rootCommand.Subcommands.Add(nestedCommand); + _rootCommand.Subcommands.Add(nestedCommand); - _testParser = new Parser(rootCommand); _testSymbolsAsString = "root_command nested_command -opt1 321"; + _configuration = CommandLineConfiguration.CreateBuilder(_rootCommand).UseDefaults().Build(); } [Benchmark] public ParseResult OneOptWithNestedCommand_Parse() - => _testParser.Parse(_testSymbolsAsString); + => _rootCommand.Parse(_testSymbolsAsString, _configuration); } } diff --git a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Directives_Suggest.cs b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Directives_Suggest.cs index 14760434fa..b24c96fcb1 100644 --- a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Directives_Suggest.cs +++ b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Directives_Suggest.cs @@ -15,7 +15,7 @@ namespace System.CommandLine.Benchmarks.CommandLine public class Perf_Parser_Directives_Suggest { private NullConsole _nullConsole; - private Parser _testParser; + private CommandLineConfiguration _configuration; [GlobalSetup] public void Setup() @@ -34,7 +34,7 @@ public void Setup() vegetableOption }; - _testParser = new CommandLineBuilder(eatCommand) + _configuration = new CommandLineBuilder(eatCommand) .UseSuggestDirective() .Build(); } @@ -47,7 +47,7 @@ public void Setup() [Benchmark] public Task InvokeSuggest() - => _testParser.InvokeAsync(TestCmdArgs, _nullConsole); + => _configuration.InvokeAsync(TestCmdArgs, _nullConsole); } } diff --git a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_NestedCommands.cs b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_NestedCommands.cs index e530aa8107..1a74cd24ed 100644 --- a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_NestedCommands.cs +++ b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_NestedCommands.cs @@ -13,8 +13,8 @@ namespace System.CommandLine.Benchmarks.CommandLine public class Perf_Parser_NestedCommands { private string _testSymbolsAsString; - private Parser _testParser; private Command _rootCommand; + private CommandLineConfiguration _configuration; /// /// 1 - cmd-root @@ -45,7 +45,7 @@ private void GenerateTestNestedCommands(Command parent, int depth, int countPerL } } - [GlobalSetup(Target = nameof(ParserFromNestedCommands_Ctor))] + [GlobalSetup] public void SetupRootCommand() { string rootCommandName = "root"; @@ -62,19 +62,10 @@ public void SetupRootCommand() } _rootCommand = rootCommand; + _configuration = CommandLineConfiguration.CreateBuilder(rootCommand).UseDefaults().Build(); ; } - [GlobalSetup(Target = nameof(Parser_Parse))] - public void SetupParser() - { - SetupRootCommand(); - _testParser = new Parser(_rootCommand); - } - - [Benchmark] - public Parser ParserFromNestedCommands_Ctor() => new(_rootCommand); - [Benchmark] - public ParseResult Parser_Parse() => _testParser.Parse(_testSymbolsAsString); + public ParseResult Parser_Parse() => Parser.Parse(_rootCommand, _testSymbolsAsString, _configuration); } } diff --git a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Options_Bare.cs b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Options_Bare.cs index ca6d9d3a70..67f0d5e3b4 100644 --- a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Options_Bare.cs +++ b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Options_Bare.cs @@ -17,7 +17,7 @@ public class Perf_Parser_Options_Bare { private IEnumerable