Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -35,6 +36,7 @@ public EnumValueOptionAttribute(
isRequired,
isHidden,
isFinal,
ignoreCaseInChoices,
aliases,
choices)
{
Expand All @@ -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)
Expand All @@ -62,6 +65,7 @@ public EnumValueOptionAttribute(
isRequired,
isHidden,
isFinal,
ignoreCaseInChoices,
aliases)
{
UseDefaultChoices = useDefaultChoices;
Expand All @@ -85,6 +89,7 @@ public override ICommonOption CreateOption(object source, PropertyInfo propertyI
IsRequired,
IsHidden,
IsFinal,
IgnoreCaseInChoices,
UseDefaultChoices,
Aliases,
Choices,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -36,9 +38,11 @@ public MultipleValueOptionAttribute(
isRequired,
isHidden,
isFinal,
ignoreCaseInChoices,
aliases,
choices: null)
{
IgnoreOrderInChoices = ignoreOrderInChoices;
ContextCapture = CreateContextCapture(contextCaptureType, numberOfItemsToCapture);
}

Expand All @@ -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)
Expand All @@ -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<Type> ValidPropertyTypes => [typeof(IList<T>)];
Expand Down Expand Up @@ -98,6 +107,8 @@ public override ICommonOption CreateOption(object source, PropertyInfo propertyI
IsRequired,
IsHidden,
IsFinal,
IgnoreCaseInChoices,
IgnoreOrderInChoices,
Aliases,
choices: null,
DefaultValue,
Expand Down
9 changes: 9 additions & 0 deletions Core/NetArgumentParser/src/Attributes/ValueOptionAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -34,6 +35,7 @@ public ValueOptionAttribute(
isRequired,
isHidden,
isFinal,
ignoreCaseInChoices,
aliases)
{
DefaultValue = new DefaultOptionValue<T>(defaultValue);
Expand All @@ -48,6 +50,7 @@ public ValueOptionAttribute(
bool isRequired = false,
bool isHidden = false,
bool isFinal = false,
bool ignoreCaseInChoices = false,
string[]? aliases = null)
: this(
choices: null,
Expand All @@ -58,6 +61,7 @@ public ValueOptionAttribute(
isRequired,
isHidden,
isFinal,
ignoreCaseInChoices,
aliases)
{
}
Expand All @@ -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)),
Expand All @@ -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<T>? Choices { get; }
public DefaultOptionValue<T>? DefaultValue { get; }

Expand All @@ -109,6 +117,7 @@ public override ICommonOption CreateOption(object source, PropertyInfo propertyI
IsRequired,
IsHidden,
IsFinal,
IgnoreCaseInChoices,
Aliases,
Choices,
DefaultValue,
Expand Down
35 changes: 35 additions & 0 deletions Core/NetArgumentParser/src/Extensions/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
@@ -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<T>(this IEnumerable<T> enumerable, IEnumerable<T> 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<T>(
this IEnumerable<string> enumerable,
IEnumerable<string> other,
IEqualityComparer<string>? stringComparer = null)
{
ExtendedArgumentNullException.ThrowIfNull(enumerable, nameof(enumerable));
ExtendedArgumentNullException.ThrowIfNull(other, nameof(other));

stringComparer ??= EqualityComparer<string>.Default;

return Enumerable.SequenceEqual(
enumerable.OrderBy(t => t),
other.OrderBy(t => t),
stringComparer);
}
}
2 changes: 2 additions & 0 deletions Core/NetArgumentParser/src/Options/EnumValueOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public EnumValueOption(
bool isRequired = false,
bool isHidden = false,
bool isFinal = false,
bool ignoreCaseInChoices = false,
bool useDefaultChoices = true,
IEnumerable<string>? aliases = null,
IEnumerable<T>? choices = null,
Expand All @@ -33,6 +34,7 @@ public EnumValueOption(
isRequired,
isHidden,
isFinal,
ignoreCaseInChoices,
aliases,
useDefaultChoices && choices is null
? (valueRestriction is not null
Expand Down
40 changes: 39 additions & 1 deletion Core/NetArgumentParser/src/Options/MultipleValueOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>,
System.Collections.Generic.IEnumerable<string>,
System.Collections.Generic.IEqualityComparer<string>,
bool>;

public class MultipleValueOption<T> : ValueOption<IList<T>>
{
public MultipleValueOption(
Expand All @@ -17,6 +25,8 @@ public MultipleValueOption(
bool isRequired = false,
bool isHidden = false,
bool isFinal = false,
bool ignoreCaseInChoices = false,
bool ignoreOrderInChoices = false,
IEnumerable<string>? aliases = null,
IEnumerable<IList<T>>? choices = null,
DefaultOptionValue<IList<T>>? defaultValue = null,
Expand All @@ -32,15 +42,19 @@ public MultipleValueOption(
isRequired,
isHidden,
isFinal,
ignoreCaseInChoices,
aliases,
choices,
defaultValue,
valueRestriction,
afterValueParsingAction,
contextCapture ?? new ZeroOrMoreContextCapture())
{
IgnoreOrderInChoices = ignoreOrderInChoices;
}

public bool IgnoreOrderInChoices { get; }

public override string GetShortExample()
{
string extendedMetavariable = GetExtendedMetavariable();
Expand Down Expand Up @@ -106,6 +120,30 @@ protected override string[] GetAllowedValues()
protected override bool IsValueSatisfyChoices(IList<T> 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<string>? castedValue = value as IList<string>;
IEnumerable<IList<string>> choices = Choices.Cast<IList<string>>();

StringEnumerableComparer enumerableComparer = IgnoreOrderInChoices
? EnumerableExtensions.ScrambledEquals<string>
: Enumerable.SequenceEqual;

return choices.Any(t =>
{
var stringComparer = new StringEqualityComparer(StringComparison.OrdinalIgnoreCase);
return enumerableComparer.Invoke(t, castedValue!, stringComparer);
});
}

Func<IList<T>, IList<T>, bool> comparer = IgnoreOrderInChoices
? EnumerableExtensions.ScrambledEquals
: Enumerable.SequenceEqual;

return Choices.Any(t => comparer.Invoke(t, value));
}
}
22 changes: 20 additions & 2 deletions Core/NetArgumentParser/src/Options/ValueOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using NetArgumentParser.Converters;
using NetArgumentParser.Options.Context;
using NetArgumentParser.Utils;
using NetArgumentParser.Utils.Comparers;

namespace NetArgumentParser.Options;

Expand All @@ -22,6 +23,7 @@ public ValueOption(
bool isRequired = false,
bool isHidden = false,
bool isFinal = false,
bool ignoreCaseInChoices = false,
IEnumerable<string>? aliases = null,
IEnumerable<T>? choices = null,
DefaultOptionValue<T>? defaultValue = null,
Expand All @@ -36,6 +38,7 @@ public ValueOption(
isRequired,
isHidden,
isFinal,
ignoreCaseInChoices,
aliases,
choices,
defaultValue,
Expand All @@ -53,6 +56,7 @@ protected ValueOption(
bool isRequired = false,
bool isHidden = false,
bool isFinal = false,
bool ignoreCaseInChoices = false,
IEnumerable<string>? aliases = null,
IEnumerable<T>? choices = null,
DefaultOptionValue<T>? defaultValue = null,
Expand Down Expand Up @@ -97,6 +101,7 @@ protected ValueOption(
? GetDefaultMetaVariable()
: metaVariable;

IgnoreCaseInChoices = ignoreCaseInChoices;
DefaultValue = defaultValue;
ValueRestriction = valueRestriction;

Expand All @@ -107,6 +112,7 @@ protected ValueOption(
public event EventHandler<OptionValueEventArgs<T>>? ValueParsed;

public string MetaVariable { get; }
public bool IgnoreCaseInChoices { get; }

public DefaultOptionValue<T>? DefaultValue { get; set; }
public OptionValueRestriction<T>? ValueRestriction { get; set; }
Expand Down Expand Up @@ -157,7 +163,7 @@ public void AddChoicesToDescription(
arraySeparator,
arrayPrefix,
arrayPostfix,
Choices);
_choices);

Description += $"{prefix}{choices}{postfix}";

Expand Down Expand Up @@ -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<string> choices = _choices.Cast<string>();
StringEqualityComparer comparer = new(StringComparison.OrdinalIgnoreCase);

return choices.Contains(value as string, comparer);
}

return _choices.Contains(value);
}

protected virtual bool IsValueSatisfyRestriction(T value)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;

namespace NetArgumentParser.Utils.Comparers;

internal class StringEqualityComparer : IEqualityComparer<string?>
{
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);
}
}
Loading