diff --git a/Core/NetArgumentParser/src/Attributes/EnumValueOptionAttribute.cs b/Core/NetArgumentParser/src/Attributes/EnumValueOptionAttribute.cs index 9a0eb98..a07e64d 100644 --- a/Core/NetArgumentParser/src/Attributes/EnumValueOptionAttribute.cs +++ b/Core/NetArgumentParser/src/Attributes/EnumValueOptionAttribute.cs @@ -23,6 +23,7 @@ public EnumValueOptionAttribute( bool isRequired = false, bool isHidden = false, bool isFinal = false, + bool ignoreCaseInChoices = false, bool useDefaultChoices = true, string[]? aliases = null, T[]? choices = null) @@ -35,6 +36,7 @@ public EnumValueOptionAttribute( isRequired, isHidden, isFinal, + ignoreCaseInChoices, aliases, choices) { @@ -50,6 +52,7 @@ public EnumValueOptionAttribute( bool isRequired = false, bool isHidden = false, bool isFinal = false, + bool ignoreCaseInChoices = false, bool useDefaultChoices = true, string[]? aliases = null, T[]? choices = null) @@ -62,6 +65,7 @@ public EnumValueOptionAttribute( isRequired, isHidden, isFinal, + ignoreCaseInChoices, aliases) { UseDefaultChoices = useDefaultChoices; @@ -85,6 +89,7 @@ public override ICommonOption CreateOption(object source, PropertyInfo propertyI IsRequired, IsHidden, IsFinal, + IgnoreCaseInChoices, UseDefaultChoices, Aliases, Choices, diff --git a/Core/NetArgumentParser/src/Attributes/MultipleValueOptionAttribute.cs b/Core/NetArgumentParser/src/Attributes/MultipleValueOptionAttribute.cs index 14ee560..7e361cb 100644 --- a/Core/NetArgumentParser/src/Attributes/MultipleValueOptionAttribute.cs +++ b/Core/NetArgumentParser/src/Attributes/MultipleValueOptionAttribute.cs @@ -24,6 +24,8 @@ public MultipleValueOptionAttribute( bool isRequired = false, bool isHidden = false, bool isFinal = false, + bool ignoreCaseInChoices = false, + bool ignoreOrderInChoices = false, string[]? aliases = null, ContextCaptureType contextCaptureType = ContextCaptureType.None, int numberOfItemsToCapture = -1) @@ -36,9 +38,11 @@ public MultipleValueOptionAttribute( isRequired, isHidden, isFinal, + ignoreCaseInChoices, aliases, choices: null) { + IgnoreOrderInChoices = ignoreOrderInChoices; ContextCapture = CreateContextCapture(contextCaptureType, numberOfItemsToCapture); } @@ -50,6 +54,8 @@ public MultipleValueOptionAttribute( bool isRequired = false, bool isHidden = false, bool isFinal = false, + bool ignoreCaseInChoices = false, + bool ignoreOrderInChoices = false, string[]? aliases = null, ContextCaptureType contextCaptureType = ContextCaptureType.None, int numberOfItemsToCapture = -1) @@ -62,12 +68,15 @@ public MultipleValueOptionAttribute( isRequired, isHidden, isFinal, + ignoreCaseInChoices, aliases) { + IgnoreOrderInChoices = ignoreOrderInChoices; ContextCapture = CreateContextCapture(contextCaptureType, numberOfItemsToCapture); } #pragma warning restore CA1019 // Define accessors for attribute arguments + public bool IgnoreOrderInChoices { get; } public IContextCapture? ContextCapture { get; } protected override IReadOnlyList ValidPropertyTypes => [typeof(IList)]; @@ -98,6 +107,8 @@ public override ICommonOption CreateOption(object source, PropertyInfo propertyI IsRequired, IsHidden, IsFinal, + IgnoreCaseInChoices, + IgnoreOrderInChoices, Aliases, choices: null, DefaultValue, diff --git a/Core/NetArgumentParser/src/Attributes/ValueOptionAttribute.cs b/Core/NetArgumentParser/src/Attributes/ValueOptionAttribute.cs index 24b47ff..cd42063 100644 --- a/Core/NetArgumentParser/src/Attributes/ValueOptionAttribute.cs +++ b/Core/NetArgumentParser/src/Attributes/ValueOptionAttribute.cs @@ -23,6 +23,7 @@ public ValueOptionAttribute( bool isRequired = false, bool isHidden = false, bool isFinal = false, + bool ignoreCaseInChoices = false, string[]? aliases = null, T[]? choices = null) : this( @@ -34,6 +35,7 @@ public ValueOptionAttribute( isRequired, isHidden, isFinal, + ignoreCaseInChoices, aliases) { DefaultValue = new DefaultOptionValue(defaultValue); @@ -48,6 +50,7 @@ public ValueOptionAttribute( bool isRequired = false, bool isHidden = false, bool isFinal = false, + bool ignoreCaseInChoices = false, string[]? aliases = null) : this( choices: null, @@ -58,6 +61,7 @@ public ValueOptionAttribute( isRequired, isHidden, isFinal, + ignoreCaseInChoices, aliases) { } @@ -71,6 +75,7 @@ public ValueOptionAttribute( bool isRequired = false, bool isHidden = false, bool isFinal = false, + bool ignoreCaseInChoices = false, string[]? aliases = null) : base( longName ?? throw new ArgumentNullException(nameof(longName)), @@ -84,10 +89,13 @@ public ValueOptionAttribute( ExtendedArgumentNullException.ThrowIfNull(metaVariable, nameof(metaVariable)); MetaVariable = metaVariable; + IgnoreCaseInChoices = ignoreCaseInChoices; Choices = choices; } public string MetaVariable { get; } + public bool IgnoreCaseInChoices { get; } + public IEnumerable? Choices { get; } public DefaultOptionValue? DefaultValue { get; } @@ -109,6 +117,7 @@ public override ICommonOption CreateOption(object source, PropertyInfo propertyI IsRequired, IsHidden, IsFinal, + IgnoreCaseInChoices, Aliases, Choices, DefaultValue, diff --git a/Core/NetArgumentParser/src/Extensions/EnumerableExtensions.cs b/Core/NetArgumentParser/src/Extensions/EnumerableExtensions.cs new file mode 100644 index 0000000..51b5695 --- /dev/null +++ b/Core/NetArgumentParser/src/Extensions/EnumerableExtensions.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NetArgumentParser.Utils.Comparers; + +namespace NetArgumentParser.Extensions; + +internal static class EnumerableExtensions +{ + internal static bool ScrambledEquals(this IEnumerable enumerable, IEnumerable other) + { + ExtendedArgumentNullException.ThrowIfNull(enumerable, nameof(enumerable)); + ExtendedArgumentNullException.ThrowIfNull(other, nameof(other)); + + return Enumerable.SequenceEqual( + enumerable.OrderBy(t => t), + other.OrderBy(t => t)); + } + + internal static bool ScrambledEquals( + this IEnumerable enumerable, + IEnumerable other, + IEqualityComparer? stringComparer = null) + { + ExtendedArgumentNullException.ThrowIfNull(enumerable, nameof(enumerable)); + ExtendedArgumentNullException.ThrowIfNull(other, nameof(other)); + + stringComparer ??= EqualityComparer.Default; + + return Enumerable.SequenceEqual( + enumerable.OrderBy(t => t), + other.OrderBy(t => t), + stringComparer); + } +} diff --git a/Core/NetArgumentParser/src/Options/EnumValueOption.cs b/Core/NetArgumentParser/src/Options/EnumValueOption.cs index 39c11f1..628accb 100644 --- a/Core/NetArgumentParser/src/Options/EnumValueOption.cs +++ b/Core/NetArgumentParser/src/Options/EnumValueOption.cs @@ -19,6 +19,7 @@ public EnumValueOption( bool isRequired = false, bool isHidden = false, bool isFinal = false, + bool ignoreCaseInChoices = false, bool useDefaultChoices = true, IEnumerable? aliases = null, IEnumerable? choices = null, @@ -33,6 +34,7 @@ public EnumValueOption( isRequired, isHidden, isFinal, + ignoreCaseInChoices, aliases, useDefaultChoices && choices is null ? (valueRestriction is not null diff --git a/Core/NetArgumentParser/src/Options/MultipleValueOption.cs b/Core/NetArgumentParser/src/Options/MultipleValueOption.cs index 3424e5b..681d717 100644 --- a/Core/NetArgumentParser/src/Options/MultipleValueOption.cs +++ b/Core/NetArgumentParser/src/Options/MultipleValueOption.cs @@ -3,10 +3,18 @@ using System.Linq; using NetArgumentParser.Configuration; using NetArgumentParser.Converters; +using NetArgumentParser.Extensions; using NetArgumentParser.Options.Context; +using NetArgumentParser.Utils.Comparers; namespace NetArgumentParser.Options; +using StringEnumerableComparer = System.Func< + System.Collections.Generic.IEnumerable, + System.Collections.Generic.IEnumerable, + System.Collections.Generic.IEqualityComparer, + bool>; + public class MultipleValueOption : ValueOption> { public MultipleValueOption( @@ -17,6 +25,8 @@ public MultipleValueOption( bool isRequired = false, bool isHidden = false, bool isFinal = false, + bool ignoreCaseInChoices = false, + bool ignoreOrderInChoices = false, IEnumerable? aliases = null, IEnumerable>? choices = null, DefaultOptionValue>? defaultValue = null, @@ -32,6 +42,7 @@ public MultipleValueOption( isRequired, isHidden, isFinal, + ignoreCaseInChoices, aliases, choices, defaultValue, @@ -39,8 +50,11 @@ public MultipleValueOption( afterValueParsingAction, contextCapture ?? new ZeroOrMoreContextCapture()) { + IgnoreOrderInChoices = ignoreOrderInChoices; } + public bool IgnoreOrderInChoices { get; } + public override string GetShortExample() { string extendedMetavariable = GetExtendedMetavariable(); @@ -106,6 +120,30 @@ protected override string[] GetAllowedValues() protected override bool IsValueSatisfyChoices(IList value) { ExtendedArgumentNullException.ThrowIfNull(value, nameof(value)); - return Choices.Count == 0 || Choices.Any(t => t.SequenceEqual(value)); + + if (Choices.Count == 0) + return true; + + if (IgnoreCaseInChoices && typeof(T) == typeof(string)) + { + IList? castedValue = value as IList; + IEnumerable> choices = Choices.Cast>(); + + StringEnumerableComparer enumerableComparer = IgnoreOrderInChoices + ? EnumerableExtensions.ScrambledEquals + : Enumerable.SequenceEqual; + + return choices.Any(t => + { + var stringComparer = new StringEqualityComparer(StringComparison.OrdinalIgnoreCase); + return enumerableComparer.Invoke(t, castedValue!, stringComparer); + }); + } + + Func, IList, bool> comparer = IgnoreOrderInChoices + ? EnumerableExtensions.ScrambledEquals + : Enumerable.SequenceEqual; + + return Choices.Any(t => comparer.Invoke(t, value)); } } diff --git a/Core/NetArgumentParser/src/Options/ValueOption.cs b/Core/NetArgumentParser/src/Options/ValueOption.cs index 63a300e..6d4389e 100644 --- a/Core/NetArgumentParser/src/Options/ValueOption.cs +++ b/Core/NetArgumentParser/src/Options/ValueOption.cs @@ -6,6 +6,7 @@ using NetArgumentParser.Converters; using NetArgumentParser.Options.Context; using NetArgumentParser.Utils; +using NetArgumentParser.Utils.Comparers; namespace NetArgumentParser.Options; @@ -22,6 +23,7 @@ public ValueOption( bool isRequired = false, bool isHidden = false, bool isFinal = false, + bool ignoreCaseInChoices = false, IEnumerable? aliases = null, IEnumerable? choices = null, DefaultOptionValue? defaultValue = null, @@ -36,6 +38,7 @@ public ValueOption( isRequired, isHidden, isFinal, + ignoreCaseInChoices, aliases, choices, defaultValue, @@ -53,6 +56,7 @@ protected ValueOption( bool isRequired = false, bool isHidden = false, bool isFinal = false, + bool ignoreCaseInChoices = false, IEnumerable? aliases = null, IEnumerable? choices = null, DefaultOptionValue? defaultValue = null, @@ -97,6 +101,7 @@ protected ValueOption( ? GetDefaultMetaVariable() : metaVariable; + IgnoreCaseInChoices = ignoreCaseInChoices; DefaultValue = defaultValue; ValueRestriction = valueRestriction; @@ -107,6 +112,7 @@ protected ValueOption( public event EventHandler>? ValueParsed; public string MetaVariable { get; } + public bool IgnoreCaseInChoices { get; } public DefaultOptionValue? DefaultValue { get; set; } public OptionValueRestriction? ValueRestriction { get; set; } @@ -157,7 +163,7 @@ public void AddChoicesToDescription( arraySeparator, arrayPrefix, arrayPostfix, - Choices); + _choices); Description += $"{prefix}{choices}{postfix}"; @@ -249,7 +255,19 @@ protected virtual string[] GetAllowedValues() protected virtual bool IsValueSatisfyChoices(T value) { ExtendedArgumentNullException.ThrowIfNull(value, nameof(value)); - return _choices.Count == 0 || _choices.Contains(value); + + if (_choices.Count == 0) + return true; + + if (IgnoreCaseInChoices && typeof(T) == typeof(string)) + { + IEnumerable choices = _choices.Cast(); + StringEqualityComparer comparer = new(StringComparison.OrdinalIgnoreCase); + + return choices.Contains(value as string, comparer); + } + + return _choices.Contains(value); } protected virtual bool IsValueSatisfyRestriction(T value) diff --git a/Core/NetArgumentParser/src/Utils/Comparers/StringEqualityComparer.cs b/Core/NetArgumentParser/src/Utils/Comparers/StringEqualityComparer.cs new file mode 100644 index 0000000..0966611 --- /dev/null +++ b/Core/NetArgumentParser/src/Utils/Comparers/StringEqualityComparer.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +namespace NetArgumentParser.Utils.Comparers; + +internal class StringEqualityComparer : IEqualityComparer +{ + public StringEqualityComparer(StringComparison comparison) + { + Comparison = comparison; + } + + public StringComparison Comparison { get; } + + public bool Equals(string? x, string? y) + { + return (x is null && y is null) + || (x?.Equals(y, Comparison) ?? false); + } + + public int GetHashCode(string? obj) + { + return HashGenerator.Generate(obj); + } +} diff --git a/Documentation/OptionalArgumentsConfig.md b/Documentation/OptionalArgumentsConfig.md index bad59e1..3be4a19 100644 --- a/Documentation/OptionalArgumentsConfig.md +++ b/Documentation/OptionalArgumentsConfig.md @@ -128,6 +128,34 @@ var fileMode = new EnumValueOption("mode", "m", choices: [FileMode.Open, FileMode.Create]); ``` +You can allow values ​​to be specified for a multiple value option in any order by passing `true` as the `ignoreOrderInChoices` argument. For example, if you specified [1, 2] as a choices, the parser will allow both [1, 2] and [2, 1] to be entered. + +```cs +var numbersOption = new MultipleValueOption( + "numbers", + "n", + ignoreOrderInChoices: true, + contextCapture: new FixedContextCapture(2), + choices: [[1, 2]]); +``` + +You can also allow string data to be entered in any case by passing `true` as the `ignoreCaseInChoices` argument. For example, if you specified ["Max"] as a choices, the parser will allow the string "Max" to be entered in any case (i.e. "Max", "max", "MAX", "mAx", etc. will be considered valid data). + +```cs +var namesOption = new MultipleValueOption( + "names", + "n", + ignoreCaseInChoices: true, + contextCapture: new FixedContextCapture(1), + choices: [["Max"]]); + +var firstNameOption = new ValueOption( + "first-name", + string.Empty, + ignoreCaseInChoices: true, + choices: ["Max"]); +``` + ### Default Value You can specify a default value for the option. In this case, if the input argument list doesn't contain a matching argument, the option will be assigned its default value. Default value is only available for value options. diff --git a/Documentation/ParserGenerationUsingAttributes.md b/Documentation/ParserGenerationUsingAttributes.md index 43e4452..17f0229 100644 --- a/Documentation/ParserGenerationUsingAttributes.md +++ b/Documentation/ParserGenerationUsingAttributes.md @@ -98,6 +98,23 @@ internal class CustomParserConfig ] public List InputFiles { get; set; } + [MultipleValueOption( + longName: "persons", + shortName: "P", + description: "persons", + metaVariable: "", + isRequired: false, + isHidden: false, + isFinal: false, + ignoreCaseInChoices: true, + ignoreOrderInChoices: true, + aliases: ["ps"], + contextCaptureType: ContextCaptureType.Fixed, + numberOfItemsToCapture: 3) + ] + [OptionGroup("complex-values", "", "")] + public List Persons { get; set; } + [ValueOption( longName: "point", shortName: "p", diff --git a/Examples/NetArgumentParser.Examples.AllUseCases/src/Program.cs b/Examples/NetArgumentParser.Examples.AllUseCases/src/Program.cs index 1c4f003..406226f 100644 --- a/Examples/NetArgumentParser.Examples.AllUseCases/src/Program.cs +++ b/Examples/NetArgumentParser.Examples.AllUseCases/src/Program.cs @@ -87,6 +87,15 @@ contextCapture: new OneOrMoreContextCapture(), afterValueParsingAction: t => resultValues.InputFiles = new List(t)), + new MultipleValueOption( + longName: "persons", + shortName: string.Empty, + description: "persons that should be added to organization", + ignoreCaseInChoices: true, + ignoreOrderInChoices: true, + contextCapture: new FixedContextCapture(3), + choices: [["Max", "Robert", "Tom"], ["David", "John", "Richard"]]), + new ValueOption( longName: "angle", shortName: "a", diff --git a/Examples/NetArgumentParser.Examples.ParserGenerationUsingAttributes/src/Program.cs b/Examples/NetArgumentParser.Examples.ParserGenerationUsingAttributes/src/Program.cs index d966ad8..4d72091 100644 --- a/Examples/NetArgumentParser.Examples.ParserGenerationUsingAttributes/src/Program.cs +++ b/Examples/NetArgumentParser.Examples.ParserGenerationUsingAttributes/src/Program.cs @@ -131,6 +131,23 @@ public CustomParserConfig() [OptionGroup("complex-values", "Complex value options", "Complex value options descripton")] public List InputFiles { get; set; } + [MultipleValueOption( + longName: "persons", + shortName: "P", + description: "persons", + metaVariable: "", + isRequired: false, + isHidden: false, + isFinal: false, + ignoreCaseInChoices: true, + ignoreOrderInChoices: true, + aliases: ["ps"], + contextCaptureType: ContextCaptureType.Fixed, + numberOfItemsToCapture: 3) + ] + [OptionGroup("complex-values", "", "")] + public List Persons { get; set; } + [ValueOption( longName: "point", shortName: "p", diff --git a/Tests/NetArgumentParser.Tests/src/ArgumentParserSubcommandTests.cs b/Tests/NetArgumentParser.Tests/src/ArgumentParserSubcommandTests.cs index 2439f01..1a228b5 100644 --- a/Tests/NetArgumentParser.Tests/src/ArgumentParserSubcommandTests.cs +++ b/Tests/NetArgumentParser.Tests/src/ArgumentParserSubcommandTests.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using NetArgumentParser.Extensions; using NetArgumentParser.Informing; using NetArgumentParser.Options; using NetArgumentParser.Options.Context; using NetArgumentParser.Subcommands; -using NetArgumentParser.Tests.Extensions; using NetArgumentParser.Tests.Models; namespace NetArgumentParser.Tests; diff --git a/Tests/NetArgumentParser.Tests/src/ArgumentParserTests.cs b/Tests/NetArgumentParser.Tests/src/ArgumentParserTests.cs index 82a52e9..9eab476 100644 --- a/Tests/NetArgumentParser.Tests/src/ArgumentParserTests.cs +++ b/Tests/NetArgumentParser.Tests/src/ArgumentParserTests.cs @@ -4,10 +4,10 @@ using System.Linq; using System.Runtime.CompilerServices; using NetArgumentParser.Converters; +using NetArgumentParser.Extensions; using NetArgumentParser.Informing; using NetArgumentParser.Options; using NetArgumentParser.Options.Context; -using NetArgumentParser.Tests.Extensions; using NetArgumentParser.Tests.Models; // Necessary for using dynamic @@ -820,6 +820,128 @@ public void Parse_MultipleValueOptions_ThrowsExceptionIfValueNotSatisfyChoices() Assert.Null(ex); } + [Fact] + public void Parse_ValueOptions_DoNotThrowsExceptionIfValueNotSatisfyChoicesDueCase() + { + var arguments = new string[] + { + "--item", "aPpLe", + "--first-name", "max", + "--second-name", "SMITH" + }; + + var options = new ICommonOption[] + { + new ValueOption( + "item", + string.Empty, + ignoreCaseInChoices: true, + choices: ["Banana", "Apple", "Grape"]), + + new ValueOption( + "first-name", + string.Empty, + ignoreCaseInChoices: true, + choices: ["Max"]), + + new ValueOption( + "second-name", + string.Empty, + ignoreCaseInChoices: true, + choices: ["Smith", "Brown"]) + }; + + var parser = new ArgumentParser(); + parser.AddOptions(options); + + Exception? ex = Record.Exception(() => parser.Parse(arguments)); + Assert.Null(ex); + } + + [Fact] + public void Parse_MultipleValueOptions_DoNotThrowsExceptionIfValueNotSatisfyChoicesDueCase() + { + var arguments = new string[] + { + "--items", "bAnAnA", "apple", "GRAPE" + }; + + var options = new ICommonOption[] + { + new MultipleValueOption( + "items", + "i", + ignoreCaseInChoices: true, + contextCapture: new ZeroOrMoreContextCapture(), + choices: [["Banana", "Apple", "Grape"], ["Cucumber", "Tomato"]]) + }; + + var parser = new ArgumentParser(); + parser.AddOptions(options); + + Exception? ex = Record.Exception(() => parser.Parse(arguments)); + Assert.Null(ex); + } + + [Fact] + public void Parse_MultipleValueOptions_DoNotThrowsExceptionIfValueNotSatisfyChoicesDueOrder() + { + var arguments = new string[] + { + "-n", "7", "0", "1", "0", + "--items", "Apple", "Grape", "Banana" + }; + + var options = new ICommonOption[] + { + new MultipleValueOption( + "numbers", + "n", + ignoreOrderInChoices: true, + contextCapture: new FixedContextCapture(4), + choices: [[1, 2, 3, 4], [0, 0, 7, 1]]), + + new MultipleValueOption( + "items", + "i", + ignoreOrderInChoices: true, + contextCapture: new ZeroOrMoreContextCapture(), + choices: [["Banana", "Apple", "Grape"], ["Cucumber", "Tomato"]]) + }; + + var parser = new ArgumentParser(); + parser.AddOptions(options); + + Exception? ex = Record.Exception(() => parser.Parse(arguments)); + Assert.Null(ex); + } + + [Fact] + public void Parse_MultipleValueOptions_DoNotThrowsExceptionIfValueNotSatisfyChoicesDueCaseAndOrder() + { + var arguments = new string[] + { + "--items", "apple", "GRAPE", "bAnAnA" + }; + + var options = new ICommonOption[] + { + new MultipleValueOption( + "items", + "i", + ignoreCaseInChoices: true, + ignoreOrderInChoices: true, + contextCapture: new ZeroOrMoreContextCapture(), + choices: [["Banana", "Apple", "Grape"], ["Cucumber", "Tomato"]]) + }; + + var parser = new ArgumentParser(); + parser.AddOptions(options); + + Exception? ex = Record.Exception(() => parser.Parse(arguments)); + Assert.Null(ex); + } + [Fact] public void Parse_OptionsWithAliases_OptionsHandledCorrectly() { diff --git a/Tests/NetArgumentParser.Tests/src/Extensions/EnumerableExtensions.cs b/Tests/NetArgumentParser.Tests/src/Extensions/EnumerableExtensions.cs deleted file mode 100644 index 624f950..0000000 --- a/Tests/NetArgumentParser.Tests/src/Extensions/EnumerableExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace NetArgumentParser.Tests.Extensions; - -internal static class EnumerableExtensions -{ - internal static bool ScrambledEquals(this IEnumerable enumerable, IEnumerable other) - { - ExtendedArgumentNullException.ThrowIfNull(enumerable, nameof(enumerable)); - ExtendedArgumentNullException.ThrowIfNull(other, nameof(other)); - - return Enumerable.SequenceEqual( - enumerable.OrderBy(t => t), - other.OrderBy(t => t)); - } -} diff --git a/Tests/NetArgumentParser.Tests/src/Models/Configurations/ComplexParserGeneratorConfig.cs b/Tests/NetArgumentParser.Tests/src/Models/Configurations/ComplexParserGeneratorConfig.cs index f29c750..8a6e753 100644 --- a/Tests/NetArgumentParser.Tests/src/Models/Configurations/ComplexParserGeneratorConfig.cs +++ b/Tests/NetArgumentParser.Tests/src/Models/Configurations/ComplexParserGeneratorConfig.cs @@ -23,6 +23,7 @@ internal class ComplexParserGeneratorConfig public const bool ModeIsRequired = true; public const bool ModeIsHidden = false; public const bool ModeIsFinal = false; + public const bool ModeIgnoreCaseInChoices = false; public const bool ModeUseDefaultChoices = true; public const string IgnoreCaseLongName = "ignore-case"; @@ -39,6 +40,8 @@ internal class ComplexParserGeneratorConfig public const bool InputFilesIsRequired = true; public const bool InputFilesIsHidden = false; public const bool InputFilesIsFinal = false; + public const bool InputFilesIgnoreCaseInChoices = false; + public const bool InputFilesIgnoreOrderInChoices = false; public const string MarginLongName = "margin"; public const string MarginShortName = "M"; @@ -47,6 +50,7 @@ internal class ComplexParserGeneratorConfig public const bool MarginIsRequired = false; public const bool MarginIsHidden = true; public const bool MarginIsFinal = false; + public const bool MarginIgnoreCaseInChoices = false; public const string AngleLongName = "angle"; public const string AngleShortName = "a"; @@ -55,6 +59,7 @@ internal class ComplexParserGeneratorConfig public const bool AngleIsRequired = false; public const bool AngleIsHidden = false; public const bool AngleIsFinal = false; + public const bool AngleIgnoreCaseInChoices = false; public const double AngleDefaultValue = 45; public const string SubcommandsOnlySubcommandName = "subcommands-only"; @@ -120,6 +125,7 @@ public ComplexParserGeneratorConfig() ModeIsRequired, ModeIsHidden, ModeIsFinal, + ModeIgnoreCaseInChoices, ModeUseDefaultChoices, [], [FileMode.Create, FileMode.Open]) @@ -156,6 +162,8 @@ public ComplexParserGeneratorConfig() InputFilesIsRequired, InputFilesIsHidden, InputFilesIsFinal, + InputFilesIgnoreCaseInChoices, + InputFilesIgnoreOrderInChoices, ["i1", "i2", "i3"], ContextCaptureType.OneOrMore) ] @@ -173,6 +181,7 @@ public ComplexParserGeneratorConfig() MarginIsRequired, MarginIsHidden, MarginIsFinal, + MarginIgnoreCaseInChoices, []) ] [OptionGroup( @@ -190,6 +199,7 @@ public ComplexParserGeneratorConfig() AngleIsRequired, AngleIsHidden, AngleIsFinal, + AngleIgnoreCaseInChoices, [], [0, 45, 90, 135, 180]) ] diff --git a/Tests/NetArgumentParser.Tests/src/ParserGeneratorTests.cs b/Tests/NetArgumentParser.Tests/src/ParserGeneratorTests.cs index f0e32d3..e49d8e8 100644 --- a/Tests/NetArgumentParser.Tests/src/ParserGeneratorTests.cs +++ b/Tests/NetArgumentParser.Tests/src/ParserGeneratorTests.cs @@ -5,11 +5,11 @@ using System.Linq; using System.Numerics; using NetArgumentParser.Converters; +using NetArgumentParser.Extensions; using NetArgumentParser.Generators; using NetArgumentParser.Informing; using NetArgumentParser.Options; using NetArgumentParser.Subcommands; -using NetArgumentParser.Tests.Extensions; using NetArgumentParser.Tests.Models; using NetArgumentParser.Tests.Models.Configurations; @@ -772,6 +772,10 @@ private static void VerifyComplexParserGeneratorConfigQuantum(ParserQuantum quan ComplexParserGeneratorConfig.ModeIsFinal, modeOption.IsFinal); + Assert.Equal( + ComplexParserGeneratorConfig.ModeIgnoreCaseInChoices, + modeOption.IgnoreCaseInChoices); + Assert.Equal( ComplexParserGeneratorConfig.ModeAliases, modeOption.Aliases); @@ -861,6 +865,14 @@ private static void VerifyComplexParserGeneratorConfigQuantum(ParserQuantum quan ComplexParserGeneratorConfig.InputFilesIsFinal, inputFilesOption.IsFinal); + Assert.Equal( + ComplexParserGeneratorConfig.InputFilesIgnoreCaseInChoices, + inputFilesOption.IgnoreCaseInChoices); + + Assert.Equal( + ComplexParserGeneratorConfig.InputFilesIgnoreOrderInChoices, + inputFilesOption.IgnoreOrderInChoices); + Assert.Equal( ComplexParserGeneratorConfig.InputFilesAliases, inputFilesOption.Aliases); @@ -902,6 +914,10 @@ private static void VerifyComplexParserGeneratorConfigQuantum(ParserQuantum quan ComplexParserGeneratorConfig.MarginIsFinal, marginOption.IsFinal); + Assert.Equal( + ComplexParserGeneratorConfig.MarginIgnoreCaseInChoices, + marginOption.IgnoreCaseInChoices); + Assert.Equal( ComplexParserGeneratorConfig.MarginAliases, marginOption.Aliases); @@ -943,6 +959,10 @@ private static void VerifyComplexParserGeneratorConfigQuantum(ParserQuantum quan ComplexParserGeneratorConfig.AngleIsFinal, angleOption.IsFinal); + Assert.Equal( + ComplexParserGeneratorConfig.AngleIgnoreCaseInChoices, + angleOption.IgnoreCaseInChoices); + Assert.Equal( ComplexParserGeneratorConfig.AngleDefaultValue, angleOption.DefaultValue?.Value);