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 dafb0cfd4c..b4f8bea74f 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 @@ -5,9 +5,17 @@ System.CommandLine public System.Boolean HasDefaultValue { get; } public System.String HelpName { get; set; } public System.Type ValueType { get; } + public Argument AcceptLegalFileNamesOnly() + public Argument AcceptLegalFilePathsOnly() + public Argument AcceptOnlyFromAmong(System.String[] values) + public Argument AddCompletions(System.String[] completions) + public Argument AddCompletions(System.Func> completionsDelegate) + public Argument AddCompletions(System.Func> completionsDelegate) public System.Void AddValidator(System.Action validate) 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.Void SetDefaultValue(System.Object value) public System.Void SetDefaultValueFactory(System.Func defaultValueFactory) public System.Void SetDefaultValueFactory(System.Func defaultValueFactory) @@ -32,19 +40,11 @@ System.CommandLine public System.Boolean Equals(ArgumentArity other) public System.Boolean Equals(System.Object obj) public System.Int32 GetHashCode() - public static class ArgumentExtensions - public static TArgument AddCompletions(this TArgument argument, System.String[] values) - public static TArgument AddCompletions(this TArgument argument, System.Func> complete) - public static TArgument AddCompletions(this TArgument argument, System.Func> complete) - public static Argument ExistingOnly(this Argument argument) - public static Argument ExistingOnly(this Argument argument) - public static Argument ExistingOnly(this Argument argument) - public static Argument ExistingOnly(this Argument argument) - public static TArgument FromAmong(this TArgument argument, System.String[] values) - public static TArgument LegalFileNamesOnly(this TArgument argument) - public static TArgument LegalFilePathsOnly(this TArgument argument) - public static ParseResult Parse(this Argument argument, System.String commandLine) - public static ParseResult Parse(this Argument argument, System.String[] args) + public static class ArgumentValidation + public static Argument AcceptExistingOnly(this Argument argument) + public static Argument AcceptExistingOnly(this Argument argument) + public static Argument AcceptExistingOnly(this Argument argument) + public static Argument AcceptExistingOnly(this Argument argument) public class Command : IdentifierSymbol, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable .ctor(System.String name, System.String description = null) public System.Collections.Generic.IReadOnlyList Arguments { get; } @@ -109,7 +109,7 @@ System.CommandLine .ctor() .ctor(System.String message, System.Exception innerException) public static class CompletionSourceExtensions - public static System.Void Add(this System.Collections.Generic.ICollection>> completionSources, System.Func> complete) + public static System.Void Add(this System.Collections.Generic.ICollection>> completionSources, System.Func> completionsDelegate) public static System.Void Add(this System.Collections.Generic.ICollection>> completionSources, System.String[] completions) public static class ConsoleExtensions public static System.Void Write(this IConsole console, System.String value) @@ -193,9 +193,17 @@ System.CommandLine public ArgumentArity Arity { get; set; } public System.Boolean IsRequired { get; set; } public System.Type ValueType { get; } + public Option AcceptLegalFileNamesOnly() + public Option AcceptLegalFilePathsOnly() + public Option AcceptOnlyFromAmong(System.String[] values) + public Option AddCompletions(System.String[] completions) + public Option AddCompletions(System.Func> completionsDelegate) + public Option AddCompletions(System.Func> completionsDelegate) public System.Void AddValidator(System.Action validate) public System.Collections.Generic.IEnumerable GetCompletions(System.CommandLine.Completions.CompletionContext context) public System.Boolean HasAliasIgnoringPrefix(System.String alias) + public ParseResult Parse(System.String commandLine) + public ParseResult Parse(System.String[] args) public System.Void SetDefaultValue(System.Object value) public System.Void SetDefaultValueFactory(System.Func defaultValueFactory) public class Option : Option, IValueDescriptor, System.CommandLine.Binding.IValueDescriptor @@ -205,19 +213,11 @@ System.CommandLine .ctor(System.String[] aliases, Func parseArgument, System.Boolean isDefault = False, System.String description = null) .ctor(System.String name, Func defaultValueFactory, System.String description = null) .ctor(System.String[] aliases, Func defaultValueFactory, System.String description = null) - public static class OptionExtensions - public static TOption AddCompletions(this TOption option, System.String[] values) - public static TOption AddCompletions(this TOption option, System.Func> complete) - public static TOption AddCompletions(this TOption option, System.Func> complete) - public static Option ExistingOnly(this Option option) - public static Option ExistingOnly(this Option option) - public static Option ExistingOnly(this Option option) - public static Option ExistingOnly(this Option option) - public static TOption FromAmong(this TOption option, System.String[] values) - public static TOption LegalFileNamesOnly(this TOption option) - public static TOption LegalFilePathsOnly(this TOption option) - public static ParseResult Parse(this Option option, System.String commandLine) - public static ParseResult Parse(this Option option, System.String[] args) + public static class OptionValidation + public static Option AcceptExistingOnly(this Option option) + public static Option AcceptExistingOnly(this Option option) + public static Option AcceptExistingOnly(this Option option) + public static Option AcceptExistingOnly(this Option option) public class ParseResult public System.CommandLine.Parsing.CommandResult CommandResult { get; } public System.Collections.Generic.IReadOnlyDictionary> Directives { get; } diff --git a/src/System.CommandLine.Suggest/SuggestionDispatcher.cs b/src/System.CommandLine.Suggest/SuggestionDispatcher.cs index 0a350926d3..6c36afb9e8 100644 --- a/src/System.CommandLine.Suggest/SuggestionDispatcher.cs +++ b/src/System.CommandLine.Suggest/SuggestionDispatcher.cs @@ -89,9 +89,15 @@ public SuggestionDispatcher(ISuggestionRegistration suggestionRegistration, ISug private Command GetCommand { get; } - private Option ExecutableOption { get; } = - new Option(new[] { "-e", "--executable" }, "The executable to call for suggestions") - .LegalFilePathsOnly(); + private Option ExecutableOption { get; } = GetExecutableOption(); + + private static Option GetExecutableOption() + { + var option = new Option(new[] { "-e", "--executable" }, "The executable to call for suggestions"); + option.AcceptLegalFilePathsOnly(); + + return option; + } private Command ListCommand { get; } diff --git a/src/System.CommandLine.Tests/ArgumentTests.cs b/src/System.CommandLine.Tests/ArgumentTests.cs index 84abe6f2cd..18006d1e3b 100644 --- a/src/System.CommandLine.Tests/ArgumentTests.cs +++ b/src/System.CommandLine.Tests/ArgumentTests.cs @@ -769,7 +769,7 @@ public void OnlyTake_can_pass_on_all_tokens_from_a_single_arity_argument_to_anot public void Argument_of_enum_can_limit_enum_members_as_valid_values() { var argument = new Argument() - .FromAmong(ConsoleColor.Red.ToString(), ConsoleColor.Green.ToString()); + .AcceptOnlyFromAmong(ConsoleColor.Red.ToString(), ConsoleColor.Green.ToString()); Command command = new("set-color") { argument diff --git a/src/System.CommandLine.Tests/CompletionContextTests.cs b/src/System.CommandLine.Tests/CompletionContextTests.cs index 83dfa3ec35..4090dfd430 100644 --- a/src/System.CommandLine.Tests/CompletionContextTests.cs +++ b/src/System.CommandLine.Tests/CompletionContextTests.cs @@ -112,7 +112,7 @@ public void When_position_is_greater_than_input_length_in_a_string_command_line_ var command = new Command("the-command") { new Argument(), - new Option("--option1").FromAmong("apple", "banana", "cherry", "durian"), + new Option("--option1").AcceptOnlyFromAmong("apple", "banana", "cherry", "durian"), new Option("--option2") }; @@ -178,7 +178,7 @@ public void When_position_is_unspecified_in_array_command_line_and_final_token_m { var command = new Command("the-command") { - new Option("--option1").FromAmong("apple", "banana", "cherry", "durian"), + new Option("--option1").AcceptOnlyFromAmong("apple", "banana", "cherry", "durian"), new Option("--option2"), new Argument() }; diff --git a/src/System.CommandLine.Tests/CompletionTests.cs b/src/System.CommandLine.Tests/CompletionTests.cs index 67e59ea177..0597bb6d94 100644 --- a/src/System.CommandLine.Tests/CompletionTests.cs +++ b/src/System.CommandLine.Tests/CompletionTests.cs @@ -438,8 +438,8 @@ public void Parser_options_can_supply_context_sensitive_matches() { var parser = new RootCommand { - new Option("--bread").FromAmong("wheat", "sourdough", "rye"), - new Option("--cheese").FromAmong("provolone", "cheddar", "cream cheese") + new Option("--bread").AcceptOnlyFromAmong("wheat", "sourdough", "rye"), + new Option("--cheese").AcceptOnlyFromAmong("provolone", "cheddar", "cream cheese") }; var commandLine = "--bread"; @@ -543,8 +543,8 @@ public void Argument_completions_can_be_based_on_the_proximate_option() var parser = new Parser( new Command("outer") { - new Option("--one").FromAmong("one-a", "one-b"), - new Option("--two").FromAmong("two-a", "two-b") + new Option("--one").AcceptOnlyFromAmong("one-a", "one-b"), + new Option("--two").AcceptOnlyFromAmong("two-a", "two-b") }); var commandLine = "outer --two"; @@ -642,11 +642,11 @@ public void When_caller_does_the_tokenizing_then_argument_completions_are_based_ var command = new Command("outer") { new Option("one") - .FromAmong("one-a", "one-b", "one-c"), + .AcceptOnlyFromAmong("one-a", "one-b", "one-c"), new Option("two") - .FromAmong("two-a", "two-b", "two-c"), + .AcceptOnlyFromAmong("two-a", "two-b", "two-c"), new Option("three") - .FromAmong("three-a", "three-b", "three-c") + .AcceptOnlyFromAmong("three-a", "three-b", "three-c") }; var parser = new CommandLineBuilder(new RootCommand @@ -669,11 +669,11 @@ public void When_parsing_from_array_then_argument_completions_are_based_on_the_p var command = new Command("outer") { new Option("one") - .FromAmong("one-a", "one-b", "one-c"), + .AcceptOnlyFromAmong("one-a", "one-b", "one-c"), new Option("two") - .FromAmong("two-a", "two-b", "two-c"), + .AcceptOnlyFromAmong("two-a", "two-b", "two-c"), new Option("three") - .FromAmong("three-a", "three-b", "three-c") + .AcceptOnlyFromAmong("three-a", "three-b", "three-c") }; var result = command.Parse("outer two b"); @@ -691,15 +691,15 @@ public void When_parsing_from_text_then_argument_completions_are_based_on_the_pr { new Command("one") { - new Argument().FromAmong("one-a", "one-b", "one-c") + new Argument().AcceptOnlyFromAmong("one-a", "one-b", "one-c") }, new Command("two") { - new Argument().FromAmong("two-a", "two-b", "two-c") + new Argument().AcceptOnlyFromAmong("two-a", "two-b", "two-c") }, new Command("three") { - new Argument().FromAmong("three-a", "three-b", "three-c") + new Argument().AcceptOnlyFromAmong("three-a", "three-b", "three-c") } }; @@ -718,15 +718,15 @@ public void When_parsing_from_array_then_argument_completions_are_based_on_the_p { new Command("one") { - new Argument().FromAmong("one-a", "one-b", "one-c") + new Argument().AcceptOnlyFromAmong("one-a", "one-b", "one-c") }, new Command("two") { - new Argument().FromAmong("two-a", "two-b", "two-c") + new Argument().AcceptOnlyFromAmong("two-a", "two-b", "two-c") }, new Command("three") { - new Argument().FromAmong("three-a", "three-b", "three-c") + new Argument().AcceptOnlyFromAmong("three-a", "three-b", "three-c") } }; @@ -743,8 +743,8 @@ public void When_parsing_from_text_if_the_proximate_option_is_completed_then_com { var command = new RootCommand { - new Option("--framework").FromAmong("net7.0"), - new Option("--language").FromAmong("C#"), + new Option("--framework").AcceptOnlyFromAmong("net7.0"), + new Option("--language").AcceptOnlyFromAmong("C#"), new Option("--langVersion") }; var parser = new CommandLineBuilder(command).Build(); @@ -760,8 +760,8 @@ public void When_parsing_from_array_if_the_proximate_option_is_completed_then_co { var command = new RootCommand { - new Option("--framework").FromAmong("net7.0"), - new Option("--language").FromAmong("C#"), + new Option("--framework").AcceptOnlyFromAmong("net7.0"), + new Option("--language").AcceptOnlyFromAmong("C#"), new Option("--langVersion") }; var parser = new CommandLineBuilder(command).Build(); diff --git a/src/System.CommandLine.Tests/OptionTests.cs b/src/System.CommandLine.Tests/OptionTests.cs index 1812bffe71..a4d4fd37f9 100644 --- a/src/System.CommandLine.Tests/OptionTests.cs +++ b/src/System.CommandLine.Tests/OptionTests.cs @@ -375,7 +375,7 @@ public void Option_of_boolean_defaults_to_false_when_not_specified() public void Option_of_enum_can_limit_enum_members_as_valid_values() { var option = new Option("--color") - .FromAmong(ConsoleColor.Red.ToString(), ConsoleColor.Green.ToString()); + .AcceptOnlyFromAmong(ConsoleColor.Red.ToString(), ConsoleColor.Green.ToString()); var result = option.Parse("--color Fuschia"); diff --git a/src/System.CommandLine.Tests/ParseDiagramTests.cs b/src/System.CommandLine.Tests/ParseDiagramTests.cs index e1cfd8bf45..c5812b27b0 100644 --- a/src/System.CommandLine.Tests/ParseDiagramTests.cs +++ b/src/System.CommandLine.Tests/ParseDiagramTests.cs @@ -34,7 +34,7 @@ public void Parse_result_diagram_displays_unmatched_tokens() { var command = new Command("command") { - new Option("-x").FromAmong("arg1", "arg2", "arg3") + new Option("-x").AcceptOnlyFromAmong("arg1", "arg2", "arg3") }; var result = command.Parse("command -x ar"); diff --git a/src/System.CommandLine.Tests/ParserTests.cs b/src/System.CommandLine.Tests/ParserTests.cs index 6878f12328..103a10f646 100644 --- a/src/System.CommandLine.Tests/ParserTests.cs +++ b/src/System.CommandLine.Tests/ParserTests.cs @@ -351,7 +351,7 @@ public void Parser_root_Options_can_be_specified_multiple_times_and_their_argume public void Options_can_be_specified_multiple_times_and_their_arguments_are_collated() { var animalsOption = new Option(new[] { "-a", "--animals" }) - .FromAmong("dog", "cat", "sheep"); + .AcceptOnlyFromAmong("dog", "cat", "sheep"); var vegetablesOption = new Option(new[] { "-v", "--vegetables" }); var parser = new Parser( new Command("the-command") { diff --git a/src/System.CommandLine.Tests/ParsingValidationTests.cs b/src/System.CommandLine.Tests/ParsingValidationTests.cs index 1416cb282e..2d66518ac3 100644 --- a/src/System.CommandLine.Tests/ParsingValidationTests.cs +++ b/src/System.CommandLine.Tests/ParsingValidationTests.cs @@ -25,7 +25,7 @@ public ParsingValidationTests(ITestOutputHelper output) public void When_an_option_accepts_only_specific_arguments_but_a_wrong_one_is_supplied_then_an_informative_error_is_returned() { var option = new Option("-x") - .FromAmong("this", "that", "the-other-thing"); + .AcceptOnlyFromAmong("this", "that", "the-other-thing"); var result = option.Parse("-x none-of-those"); @@ -41,7 +41,7 @@ public void When_an_option_accepts_only_specific_arguments_but_a_wrong_one_is_su public void When_an_option_has_en_error_then_the_error_has_a_reference_to_the_option() { var option = new Option("-x") - .FromAmong("this", "that"); + .AcceptOnlyFromAmong("this", "that"); var result = option.Parse("-x something_else"); @@ -54,7 +54,7 @@ public void When_an_option_has_en_error_then_the_error_has_a_reference_to_the_op [Fact] // https://github.com/dotnet/command-line-api/issues/1475 public void When_FromAmong_is_used_then_the_OptionResult_ErrorMessage_is_set() { - var option = new Option("--opt").FromAmong("a", "b"); + var option = new Option("--opt").AcceptOnlyFromAmong("a", "b"); var command = new Command("test") { option }; var parseResult = command.Parse("test --opt c"); @@ -71,7 +71,7 @@ public void When_FromAmong_is_used_then_the_OptionResult_ErrorMessage_is_set() [Fact] // https://github.com/dotnet/command-line-api/issues/1475 public void When_FromAmong_is_used_then_the_ArgumentResult_ErrorMessage_is_set() { - var option = new Argument().FromAmong("a", "b"); + var option = new Argument().AcceptOnlyFromAmong("a", "b"); var command = new Command("test") { option }; var parseResult = command.Parse("test c"); @@ -90,8 +90,8 @@ public void When_FromAmong_is_used_for_multiple_arguments_and_valid_input_is_pro { var command = new Command("set") { - new Argument("key").FromAmong("key1", "key2"), - new Argument("value").FromAmong("value1", "value2") + new Argument("key").AcceptOnlyFromAmong("key1", "key2"), + new Argument("value").AcceptOnlyFromAmong("value1", "value2") }; var result = command.Parse("set key1 value1"); @@ -104,8 +104,8 @@ public void When_FromAmong_is_used_for_multiple_arguments_and_invalid_input_is_p { var command = new Command("set") { - new Argument("key").FromAmong("key1", "key2"), - new Argument("value").FromAmong("value1", "value2") + new Argument("key").AcceptOnlyFromAmong("key1", "key2"), + new Argument("value").AcceptOnlyFromAmong("value1", "value2") }; var result = command.Parse("set not-key1 value1"); @@ -124,8 +124,8 @@ public void When_FromAmong_is_used_for_multiple_arguments_and_invalid_input_is_p { var command = new Command("set") { - new Argument("key").FromAmong("key1", "key2"), - new Argument("value").FromAmong("value1", "value2") + new Argument("key").AcceptOnlyFromAmong("key1", "key2"), + new Argument("value").AcceptOnlyFromAmong("value1", "value2") }; var result = command.Parse("set key1 not-value1"); @@ -531,7 +531,7 @@ public void LegalFilePathsOnly_rejects_command_arguments_containing_invalid_path { var command = new Command("the-command") { - new Argument().LegalFilePathsOnly() + new Argument().AcceptLegalFilePathsOnly() }; var invalidCharacter = Path.GetInvalidPathChars().First(c => c != '"'); @@ -551,7 +551,7 @@ public void LegalFilePathsOnly_rejects_option_arguments_containing_invalid_path_ { var command = new Command("the-command") { - new Option("-x").LegalFilePathsOnly() + new Option("-x").AcceptLegalFilePathsOnly() }; var invalidCharacter = Path.GetInvalidPathChars().First(c => c != '"'); @@ -571,7 +571,7 @@ public void LegalFilePathsOnly_accepts_command_arguments_containing_valid_path_c { var command = new Command("the-command") { - new Argument().LegalFilePathsOnly() + new Argument().AcceptLegalFilePathsOnly() }; var validPathName = Directory.GetCurrentDirectory(); @@ -587,7 +587,7 @@ public void LegalFilePathsOnly_accepts_option_arguments_containing_valid_path_ch { var command = new Command("the-command") { - new Option("-x").LegalFilePathsOnly() + new Option("-x").AcceptLegalFilePathsOnly() }; var validPathName = Directory.GetCurrentDirectory(); @@ -606,7 +606,7 @@ public void LegalFileNamesOnly_rejects_command_arguments_containing_invalid_file { var command = new Command("the-command") { - new Argument().LegalFileNamesOnly() + new Argument().AcceptLegalFileNamesOnly() }; var invalidCharacter = Path.GetInvalidFileNameChars().First(c => c != '"'); @@ -626,7 +626,7 @@ public void LegalFileNamesOnly_rejects_option_arguments_containing_invalid_file_ { var command = new Command("the-command") { - new Option("-x").LegalFileNamesOnly() + new Option("-x").AcceptLegalFileNamesOnly() }; var invalidCharacter = Path.GetInvalidFileNameChars().First(c => c != '"'); @@ -646,7 +646,7 @@ public void LegalFileNamesOnly_accepts_command_arguments_containing_valid_file_n { var command = new Command("the-command") { - new Argument().LegalFileNamesOnly() + new Argument().AcceptLegalFileNamesOnly() }; var validFileName = Path.GetFileName(Directory.GetCurrentDirectory()); @@ -662,7 +662,7 @@ public void LegalFileNamesOnly_accepts_option_arguments_containing_valid_file_na { var command = new Command("the-command") { - new Option("-x").LegalFileNamesOnly() + new Option("-x").AcceptLegalFileNamesOnly() }; var validFileName = Path.GetFileName(Directory.GetCurrentDirectory()); @@ -681,7 +681,7 @@ public void A_command_argument_can_be_invalid_based_on_file_existence() { var command = new Command("move") { - new Argument("to").ExistingOnly() + new Argument("to").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -700,7 +700,7 @@ public void An_option_argument_can_be_invalid_based_on_file_existence() { var command = new Command("move") { - new Option("--to").ExistingOnly() + new Option("--to").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -719,7 +719,7 @@ public void A_command_argument_can_be_invalid_based_on_directory_existence() { var command = new Command("move") { - new Argument("to").ExistingOnly() + new Argument("to").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -738,7 +738,7 @@ public void An_option_argument_can_be_invalid_based_on_directory_existence() { var command = new Command("move") { - new Option("--to").ExistingOnly() + new Option("--to").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -757,7 +757,7 @@ public void A_command_argument_can_be_invalid_based_on_file_or_directory_existen { var command = new Command("move") { - new Argument().ExistingOnly() + new Argument().AcceptExistingOnly() }; var path = NonexistentPath(); @@ -776,7 +776,7 @@ public void An_option_argument_can_be_invalid_based_on_file_or_directory_existen { var command = new Command("move") { - new Option("--to").ExistingOnly() + new Option("--to").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -795,7 +795,7 @@ public void A_command_argument_with_multiple_files_can_be_invalid_based_on_file_ { var command = new Command("move") { - new Argument>("to").ExistingOnly() + new Argument>("to").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -814,7 +814,7 @@ public void An_option_argument_with_multiple_files_can_be_invalid_based_on_file_ { var command = new Command("move") { - new Option>("--to").ExistingOnly() + new Option>("--to").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -833,7 +833,7 @@ public void A_command_argument_with_multiple_directories_can_be_invalid_based_on { var command = new Command("move") { - new Argument>("to").ExistingOnly() + new Argument>("to").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -852,7 +852,7 @@ public void An_option_argument_with_multiple_directories_can_be_invalid_based_on { var command = new Command("move") { - new Option("--to").ExistingOnly() + new Option("--to").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -874,7 +874,7 @@ public void A_command_argument_with_multiple_FileSystemInfos_can_be_invalid_base new Argument("to") { Arity = ArgumentArity.ZeroOrMore - }.ExistingOnly(), + }.AcceptExistingOnly(), new Option("--to") }; @@ -892,7 +892,7 @@ public void An_option_argument_with_multiple_FileSystemInfos_can_be_invalid_base { var command = new Command("move") { - new Option("--to").ExistingOnly() + new Option("--to").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -911,7 +911,7 @@ public void A_command_argument_with_multiple_FileSystemInfos_can_be_invalid_base { var command = new Command("move") { - new Argument("to").ExistingOnly() + new Argument("to").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -930,7 +930,7 @@ public void An_option_argument_with_multiple_FileSystemInfos_can_be_invalid_base { var command = new Command("move") { - new Option("--to").ExistingOnly() + new Option("--to").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -949,7 +949,7 @@ public void Command_argument_does_not_return_errors_when_file_exists() { var command = new Command("move") { - new Argument().ExistingOnly() + new Argument().AcceptExistingOnly() }; var path = ExistingFile(); @@ -963,7 +963,7 @@ public void Option_argument_does_not_return_errors_when_file_exists() { var command = new Command("move") { - new Option("--to").ExistingOnly() + new Option("--to").AcceptExistingOnly() }; var path = ExistingFile(); @@ -977,7 +977,7 @@ public void Command_argument_does_not_return_errors_when_Directory_exists() { var command = new Command("move") { - new Argument().ExistingOnly() + new Argument().AcceptExistingOnly() }; var path = ExistingDirectory(); @@ -991,7 +991,7 @@ public void Option_argument_does_not_return_errors_when_Directory_exists() { var command = new Command("move") { - new Option("--to").ExistingOnly() + new Option("--to").AcceptExistingOnly() }; var path = ExistingDirectory(); diff --git a/src/System.CommandLine.Tests/TestApps/Trimming/Program.cs b/src/System.CommandLine.Tests/TestApps/Trimming/Program.cs index 40f6f6e3b3..a41990eaaa 100644 --- a/src/System.CommandLine.Tests/TestApps/Trimming/Program.cs +++ b/src/System.CommandLine.Tests/TestApps/Trimming/Program.cs @@ -1,7 +1,7 @@ using System.CommandLine; using System.CommandLine.Invocation; -var fileArgument = new Argument().LegalFileNamesOnly(); +var fileArgument = new Argument().AcceptLegalFileNamesOnly(); var command = new RootCommand { diff --git a/src/System.CommandLine/Argument.cs b/src/System.CommandLine/Argument.cs index 343c777742..e0c589509c 100644 --- a/src/System.CommandLine/Argument.cs +++ b/src/System.CommandLine/Argument.cs @@ -6,6 +6,7 @@ using System.CommandLine.Parsing; using System.CommandLine.Completions; using System.Linq; +using System.IO; namespace System.CommandLine { @@ -202,5 +203,124 @@ public override IEnumerable GetCompletions(CompletionContext con /// string IValueDescriptor.ValueName => Name; + + /// + /// Adds completions for the argument. + /// + /// The completions to add. + /// The configured argument. + public Argument AddCompletions(params string[] completions) + { + Completions.Add(completions); + return this; + } + + /// + /// Adds completions for the argument. + /// + /// A function that will be called to provide completions. + /// The option being extended. + public Argument AddCompletions(Func> completionsDelegate) + { + Completions.Add(completionsDelegate); + return this; + } + + /// + /// Adds completions for the argument. + /// + /// A function that will be called to provide completions. + /// The configured argument. + public Argument AddCompletions(Func> completionsDelegate) + { + Completions.Add(completionsDelegate); + return this; + } + + /// + /// Configures the argument to accept only the specified values, and to suggest them as command line completions. + /// + /// The values that are allowed for the argument. + /// The configured argument. + public Argument AcceptOnlyFromAmong(params string[] values) + { + AllowedValues?.Clear(); + AddAllowedValues(values); + Completions.Clear(); + Completions.Add(values); + + return this; + } + + /// + /// Configures the argument to accept only values representing legal file paths. + /// + /// The configured argument. + public Argument AcceptLegalFilePathsOnly() + { + var invalidPathChars = Path.GetInvalidPathChars(); + + AddValidator(result => + { + for (var i = 0; i < result.Tokens.Count; i++) + { + var token = result.Tokens[i]; + + // File class no longer check invalid character + // https://blogs.msdn.microsoft.com/jeremykuhne/2018/03/09/custom-directory-enumeration-in-net-core-2-1/ + var invalidCharactersIndex = token.Value.IndexOfAny(invalidPathChars); + + if (invalidCharactersIndex >= 0) + { + result.ErrorMessage = result.LocalizationResources.InvalidCharactersInPath(token.Value[invalidCharactersIndex]); + } + } + }); + + return this; + } + + /// + /// Configures the argument to accept only values representing legal file names. + /// + /// A parse error will result, for example, if file path separators are found in the parsed value. + /// The configured argument. + public Argument AcceptLegalFileNamesOnly() + { + var invalidFileNameChars = Path.GetInvalidFileNameChars(); + + AddValidator(result => + { + for (var i = 0; i < result.Tokens.Count; i++) + { + var token = result.Tokens[i]; + var invalidCharactersIndex = token.Value.IndexOfAny(invalidFileNameChars); + + if (invalidCharactersIndex >= 0) + { + result.ErrorMessage = result.LocalizationResources.InvalidCharactersInFileName(token.Value[invalidCharactersIndex]); + } + } + }); + + return this; + } + + /// + /// 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); } } diff --git a/src/System.CommandLine/ArgumentExtensions.cs b/src/System.CommandLine/ArgumentExtensions.cs deleted file mode 100644 index af69cba401..0000000000 --- a/src/System.CommandLine/ArgumentExtensions.cs +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections.Generic; -using System.CommandLine.Parsing; -using System.CommandLine.Completions; -using System.IO; - -namespace System.CommandLine -{ - /// - /// Provides extension methods for . - /// - public static class ArgumentExtensions - { - /// - /// Adds completions for an argument. - /// - /// The type of the argument. - /// The argument for which to add completions. - /// The completions to add. - /// The configured argument. - public static TArgument AddCompletions( - this TArgument argument, - params string[] values) - where TArgument : Argument - { - argument.Completions.Add(values); - - return argument; - } - - /// - /// Adds completions for an option. - /// - /// The type of the argument. - /// The argument for which to add completions. - /// A function that will be called to provide completions. - /// The option being extended. - public static TArgument AddCompletions( - this TArgument argument, - Func> complete) - where TArgument : Argument - { - argument.Completions.Add(complete); - - return argument; - } - - /// - /// Adds completions for an argument. - /// - /// The type of the argument. - /// The argument for which to add completions. - /// A function that will be called to provide completions. - /// The configured argument. - public static TArgument AddCompletions( - this TArgument argument, - Func> complete) - where TArgument : Argument - { - argument.Completions.Add(complete); - - return argument; - } - - /// - /// Configures an argument to accept only the specified values, and to suggest them as command line completions. - /// - /// The argument to configure. - /// The values that are allowed for the argument. - /// The type of the argument. - /// The configured argument. - public static TArgument FromAmong( - this TArgument argument, - params string[] values) - where TArgument : Argument - { - argument.AllowedValues?.Clear(); - argument.AddAllowedValues(values); - argument.Completions.Clear(); - argument.Completions.Add(values); - - return argument; - } - - /// - /// Configures an argument to accept only values corresponding to an existing file. - /// - /// The argument to configure. - /// The configured argument. - public static Argument ExistingOnly(this Argument argument) - { - argument.AddValidator(Validate.FileExists); - return argument; - } - - /// - /// Configures an argument to accept only values corresponding to an existing directory. - /// - /// The argument to configure. - /// The configured argument. - public static Argument ExistingOnly(this Argument argument) - { - argument.AddValidator(Validate.DirectoryExists); - return argument; - } - - /// - /// Configures an argument to accept only values corresponding to an existing file or directory. - /// - /// The argument to configure. - /// The configured argument. - public static Argument ExistingOnly(this Argument argument) - { - argument.AddValidator(Validate.FileOrDirectoryExists); - return argument; - } - - /// - /// Configures an argument to accept only values corresponding to a existing files or directories. - /// - /// The argument to configure. - /// The configured argument. - public static Argument ExistingOnly(this Argument argument) - where T : IEnumerable - { - if (typeof(IEnumerable).IsAssignableFrom(typeof(T))) - { - argument.AddValidator(Validate.FileExists); - } - else if (typeof(IEnumerable).IsAssignableFrom(typeof(T))) - { - argument.AddValidator(Validate.DirectoryExists); - } - else - { - argument.AddValidator(Validate.FileOrDirectoryExists); - } - - return argument; - } - - /// - /// Configures an argument to accept only values representing legal file paths. - /// - /// The argument to configure. - /// The configured argument. - public static TArgument LegalFilePathsOnly( - this TArgument argument) - where TArgument : Argument - { - var invalidPathChars = Path.GetInvalidPathChars(); - - argument.AddValidator(result => - { - for (var i = 0; i < result.Tokens.Count; i++) - { - var token = result.Tokens[i]; - - // File class no longer check invalid character - // https://blogs.msdn.microsoft.com/jeremykuhne/2018/03/09/custom-directory-enumeration-in-net-core-2-1/ - var invalidCharactersIndex = token.Value.IndexOfAny(invalidPathChars); - - if (invalidCharactersIndex >= 0) - { - result.ErrorMessage = result.LocalizationResources.InvalidCharactersInPath(token.Value[invalidCharactersIndex]); - } - } - }); - - return argument; - } - - /// - /// Configures an argument to accept only values representing legal file names. - /// - /// A parse error will result, for example, if file path separators are found in the parsed value. - /// The argument to configure. - /// The configured argument. - public static TArgument LegalFileNamesOnly( - this TArgument argument) - where TArgument : Argument - { - var invalidFileNameChars = Path.GetInvalidFileNameChars(); - - argument.AddValidator(result => - { - for (var i = 0; i < result.Tokens.Count; i++) - { - var token = result.Tokens[i]; - var invalidCharactersIndex = token.Value.IndexOfAny(invalidFileNameChars); - - if (invalidCharactersIndex >= 0) - { - result.ErrorMessage = result.LocalizationResources.InvalidCharactersInFileName(token.Value[invalidCharactersIndex]); - } - } - }); - - return argument; - } - - /// - /// Parses a command line string value using an argument. - /// - /// The command line string input will be split into tokens as if it had been passed on the command line. - /// The argument 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 Argument argument, - string commandLine) => - argument.GetOrCreateDefaultSimpleParser().Parse(commandLine); - - /// - /// Parses a command line string value using an argument. - /// - /// The argument 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 Argument argument, - string[] args) => - argument.GetOrCreateDefaultSimpleParser().Parse(args); - } -} diff --git a/src/System.CommandLine/ArgumentValidation.cs b/src/System.CommandLine/ArgumentValidation.cs new file mode 100644 index 0000000000..37282c871a --- /dev/null +++ b/src/System.CommandLine/ArgumentValidation.cs @@ -0,0 +1,71 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.IO; + +namespace System.CommandLine +{ + /// + /// Provides extension methods for . + /// + public static class ArgumentValidation + { + /// + /// Configures an argument to accept only values corresponding to an existing file. + /// + /// The argument to configure. + /// The configured argument. + public static Argument AcceptExistingOnly(this Argument argument) + { + argument.AddValidator(Validate.FileExists); + return argument; + } + + /// + /// Configures an argument to accept only values corresponding to an existing directory. + /// + /// The argument to configure. + /// The configured argument. + public static Argument AcceptExistingOnly(this Argument argument) + { + argument.AddValidator(Validate.DirectoryExists); + return argument; + } + + /// + /// Configures an argument to accept only values corresponding to an existing file or directory. + /// + /// The argument to configure. + /// The configured argument. + public static Argument AcceptExistingOnly(this Argument argument) + { + argument.AddValidator(Validate.FileOrDirectoryExists); + return argument; + } + + /// + /// Configures an argument to accept only values corresponding to a existing files or directories. + /// + /// The argument to configure. + /// The configured argument. + public static Argument AcceptExistingOnly(this Argument argument) + where T : IEnumerable + { + if (typeof(IEnumerable).IsAssignableFrom(typeof(T))) + { + argument.AddValidator(Validate.FileExists); + } + else if (typeof(IEnumerable).IsAssignableFrom(typeof(T))) + { + argument.AddValidator(Validate.DirectoryExists); + } + else + { + argument.AddValidator(Validate.FileOrDirectoryExists); + } + + return argument; + } + } +} diff --git a/src/System.CommandLine/CompletionSourceExtensions.cs b/src/System.CommandLine/CompletionSourceExtensions.cs index 4456e9d424..bfe34fd2a4 100644 --- a/src/System.CommandLine/CompletionSourceExtensions.cs +++ b/src/System.CommandLine/CompletionSourceExtensions.cs @@ -16,22 +16,22 @@ public static class CompletionSourceExtensions /// Adds a completion source using a delegate. /// /// The list of completion sources to add to. - /// The delegate to be called when calculating completions. + /// The delegate to be called when calculating completions. public static void Add( this ICollection>> completionSources, - Func> complete) + Func> completionsDelegate) { if (completionSources is null) { throw new ArgumentNullException(nameof(completionSources)); } - if (complete is null) + if (completionsDelegate is null) { - throw new ArgumentNullException(nameof(complete)); + throw new ArgumentNullException(nameof(completionsDelegate)); } - completionSources.Add(context => complete(context).Select(value => new CompletionItem(value))); + completionSources.Add(context => completionsDelegate(context).Select(value => new CompletionItem(value))); } /// diff --git a/src/System.CommandLine/Option.cs b/src/System.CommandLine/Option.cs index 8f24e496d3..e146ea0ea0 100644 --- a/src/System.CommandLine/Option.cs +++ b/src/System.CommandLine/Option.cs @@ -234,5 +234,91 @@ public override IEnumerable GetCompletions(CompletionContext con .OrderBy(item => item.SortText.IndexOfCaseInsensitive(context.WordToComplete)) .ThenBy(symbol => symbol.Label, StringComparer.OrdinalIgnoreCase); } + + /// + /// Configures the option to accept only the specified values, and to suggest them as command line completions. + /// + /// The values that are allowed for the option. + /// The configured option. + public Option AcceptOnlyFromAmong(params string[] values) + { + Argument.AllowedValues?.Clear(); + Argument.AddAllowedValues(values); + Argument.Completions.Clear(); + Argument.Completions.Add(values); + + return this; + } + + /// + /// Adds completions for the option. + /// + /// The completions to add. + /// The configured option. + public Option AddCompletions(params string[] completions) + { + Argument.Completions.Add(completions); + return this; + } + + /// + /// Adds completions for the option. + /// + /// A function that will be called to provide completions. + /// The configured option. + public Option AddCompletions(Func> completionsDelegate) + { + Argument.Completions.Add(completionsDelegate); + return this; + } + + /// + /// Adds completions for the option. + /// + /// A function that will be called to provide completions. + /// The configured option. + public Option AddCompletions(Func> completionsDelegate) + { + Argument.Completions.Add(completionsDelegate); + return this; + } + + /// + /// Configures the option to accept only values representing legal file paths. + /// + /// The configured option. + public Option AcceptLegalFilePathsOnly() + { + Argument.AcceptLegalFilePathsOnly(); + return this; + } + + /// + /// Configures the option to accept only values representing legal file names. + /// + /// A parse error will result, for example, if file path separators are found in the parsed value. + /// The configured option. + public Option AcceptLegalFileNamesOnly() + { + Argument.AcceptLegalFileNamesOnly(); + return this; + } + + /// + /// 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); } } diff --git a/src/System.CommandLine/OptionExtensions.cs b/src/System.CommandLine/OptionExtensions.cs deleted file mode 100644 index 8eb72c1288..0000000000 --- a/src/System.CommandLine/OptionExtensions.cs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections.Generic; -using System.CommandLine.Parsing; -using System.CommandLine.Completions; -using System.IO; - -namespace System.CommandLine -{ - /// - /// Provides extension methods for . - /// - public static class OptionExtensions - { - /// - /// Configures an option to accept only the specified values, and to suggest them as command line completions. - /// - /// The option to configure. - /// The values that are allowed for the option. - /// The type of the option's parsed value. - /// The configured argument. - public static TOption FromAmong( - this TOption option, - params string[] values) - where TOption : Option - { - option.Argument.AllowedValues?.Clear(); - option.Argument.AddAllowedValues(values); - option.Argument.Completions.Clear(); - option.Argument.Completions.Add(values); - - return option; - } - - /// - /// Adds completions for an option. - /// - /// The type of the . - /// The option for which to add completions. - /// The completions to add. - /// The option being extended. - public static TOption AddCompletions( - this TOption option, - params string[] values) - where TOption : Option - { - option.Argument.Completions.Add(values); - - return option; - } - - /// - /// Adds completions for an option. - /// - /// The type of the option. - /// The option for which to add completions. - /// A function that will be called to provide completions. - /// The option being extended. - public static TOption AddCompletions( - this TOption option, - Func> complete) - where TOption : Option - { - option.Argument.Completions.Add(complete); - - return option; - } - - /// - /// Adds completions for an option. - /// - /// The type of the option. - /// The option for which to add completions. - /// A function that will be called to provide completions. - /// The option being extended. - public static TOption AddCompletions( - this TOption option, - Func> complete) - where TOption : Option - { - option.Argument.Completions.Add(complete); - - return option; - } - - /// - /// Configures an option to accept only values corresponding to an existing file. - /// - /// The option to configure. - /// The option being extended. - public static Option ExistingOnly(this Option option) - { - option.Argument.AddValidator(Validate.FileExists); - return option; - } - - /// - /// Configures an option to accept only values corresponding to an existing directory. - /// - /// The option to configure. - /// The option being extended. - public static Option ExistingOnly(this Option option) - { - option.Argument.AddValidator(Validate.DirectoryExists); - return option; - } - - /// - /// Configures an option to accept only values corresponding to an existing file or directory. - /// - /// The option to configure. - /// The option being extended. - public static Option ExistingOnly(this Option option) - { - option.Argument.AddValidator(Validate.FileOrDirectoryExists); - return option; - } - - /// - /// Configures an option to accept only values corresponding to a existing files or directories. - /// - /// The option to configure. - /// The option being extended. - public static Option ExistingOnly(this Option option) - where T : IEnumerable - { - if (option.Argument is Argument arg) - { - arg.ExistingOnly(); - } - - return option; - } - - /// - /// Configures an option to accept only values representing legal file paths. - /// - /// The option to configure. - /// The option being extended. - public static TOption LegalFilePathsOnly( - this TOption option) - where TOption : Option - { - option.Argument.LegalFilePathsOnly(); - - return option; - } - - /// - /// Configures an option to accept only values representing legal file names. - /// - /// A parse error will result, for example, if file path separators are found in the parsed value. - /// The option to configure. - /// The option being extended. - public static TOption LegalFileNamesOnly( - this TOption option) - where TOption : Option - { - option.Argument.LegalFileNamesOnly(); - - return option; - } - - /// - /// Parses a command line string value using an option. - /// - /// The command line string input will be split into tokens as if it had been passed on the command line. - /// The option 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 Option option, - string commandLine) => - option.GetOrCreateDefaultSimpleParser().Parse(commandLine); - - /// - /// Parses a command line string value using an option. - /// - /// The option to use to parse the command line input. - /// The string options to parse. - /// A parse result describing the outcome of the parse operation. - public static ParseResult Parse( - this Option option, - string[] args) => - option.GetOrCreateDefaultSimpleParser().Parse(args); - } -} \ No newline at end of file diff --git a/src/System.CommandLine/OptionValidation.cs b/src/System.CommandLine/OptionValidation.cs new file mode 100644 index 0000000000..4637028c08 --- /dev/null +++ b/src/System.CommandLine/OptionValidation.cs @@ -0,0 +1,63 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.IO; + +namespace System.CommandLine +{ + /// + /// Provides extension methods for . + /// + public static class OptionValidation + { + /// + /// Configures an option to accept only values corresponding to an existing file. + /// + /// The option to configure. + /// The option being extended. + public static Option AcceptExistingOnly(this Option option) + { + option.Argument.AddValidator(Validate.FileExists); + return option; + } + + /// + /// Configures an option to accept only values corresponding to an existing directory. + /// + /// The option to configure. + /// The option being extended. + public static Option AcceptExistingOnly(this Option option) + { + option.Argument.AddValidator(Validate.DirectoryExists); + return option; + } + + /// + /// Configures an option to accept only values corresponding to an existing file or directory. + /// + /// The option to configure. + /// The option being extended. + public static Option AcceptExistingOnly(this Option option) + { + option.Argument.AddValidator(Validate.FileOrDirectoryExists); + return option; + } + + /// + /// Configures an option to accept only values corresponding to a existing files or directories. + /// + /// The option to configure. + /// The option being extended. + public static Option AcceptExistingOnly(this Option option) + where T : IEnumerable + { + if (option.Argument is Argument arg) + { + arg.AcceptExistingOnly(); + } + + return option; + } + } +} \ No newline at end of file