Skip to content
Open
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
4 changes: 2 additions & 2 deletions System.CommandLine.v3.ncrunchsolution
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<Settings>
<AllowParallelTestExecution>True</AllowParallelTestExecution>
<CustomBuildProperties>
<Value>TargetFrameworks = net8.0</Value>
<Value>TargetFramework = net8.0</Value>
<Value>TargetFrameworks = net10.0</Value>
<Value>TargetFramework = net10.0</Value>
</CustomBuildProperties>
<EnableRDI>False</EnableRDI>
<RdiConfigured>True</RdiConfigured>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
public static Argument<T> AcceptLegalFileNamesOnly<T>(this Argument<T> argument)
public static Argument<T> AcceptLegalFilePathsOnly<T>(this Argument<T> argument)
public static Argument<T> AcceptOnlyFromAmong<T>(this Argument<T> argument, System.String[] values)
public static Argument<T> AcceptOnlyFromAmong<T>(this Argument<T> argument, System.StringComparer comparer, System.String[] values)
public class Command : Symbol, System.Collections.IEnumerable
.ctor(System.String name, System.String description = null)
public System.CommandLine.Invocation.CommandLineAction Action { get; set; }
Expand Down Expand Up @@ -99,6 +100,7 @@
public Option<T> AcceptLegalFileNamesOnly()
public Option<T> AcceptLegalFilePathsOnly()
public Option<T> AcceptOnlyFromAmong(System.String[] values)
public Option<T> AcceptOnlyFromAmong(System.StringComparer comparer, System.String[] values)
public static class OptionValidation
public static Option<System.IO.FileInfo> AcceptExistingOnly(this Option<System.IO.FileInfo> option)
public static Option<System.IO.DirectoryInfo> AcceptExistingOnly(this Option<System.IO.DirectoryInfo> option)
Expand Down
35 changes: 35 additions & 0 deletions src/System.CommandLine.Tests/ArgumentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,39 @@ public void Argument_of_enum_can_limit_enum_members_as_valid_values()
.Should()
.BeEquivalentTo(new[] { $"Argument 'Fuschia' not recognized. Must be one of:\n\t'Red'\n\t'Green'" });
}

[Fact]
public void AcceptOnlyFromAmong_with_comparer_is_case_insensitive()
{
var argument = new Argument<string>("name");
argument.AcceptOnlyFromAmong(StringComparer.OrdinalIgnoreCase, "NAME1", "NAME2");

Command command = new("run")
{
argument
};

var result = command.Parse("run name1");

result.Errors.Should().BeEmpty();
}

[Fact]
public void AcceptOnlyFromAmong_with_comparer_rejects_invalid_values()
{
var argument = new Argument<string>("name");
argument.AcceptOnlyFromAmong(StringComparer.OrdinalIgnoreCase, "NAME1", "NAME2");

Command command = new("run")
{
argument
};

var result = command.Parse("run NAME3");

result.Errors
.Select(e => e.Message)
.Should()
.BeEquivalentTo(new[] { $"Argument 'NAME3' not recognized. Must be one of:\n\t'NAME1'\n\t'NAME2'" });
}
}
25 changes: 25 additions & 0 deletions src/System.CommandLine.Tests/OptionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,31 @@ public void Option_of_enum_can_limit_enum_members_as_valid_values()
.BeEquivalentTo(new[] { $"Argument 'Fuschia' not recognized. Must be one of:\n\t'Red'\n\t'Green'" });
}

[Fact]
public void AcceptOnlyFromAmong_with_comparer_is_case_insensitive()
{
Option<string> option = new("--name");
option.AcceptOnlyFromAmong(StringComparer.OrdinalIgnoreCase, "NAME1", "NAME2");

var result = new RootCommand { option }.Parse("--name name1");

result.Errors.Should().BeEmpty();
}

[Fact]
public void AcceptOnlyFromAmong_with_comparer_rejects_invalid_values()
{
Option<string> option = new("--name");
option.AcceptOnlyFromAmong(StringComparer.OrdinalIgnoreCase, "NAME1", "NAME2");

var result = new RootCommand { option }.Parse("--name NAME3");

result.Errors
.Select(e => e.Message)
.Should()
.BeEquivalentTo(new[] { $"Argument 'NAME3' not recognized. Must be one of:\n\t'NAME1'\n\t'NAME2'" });
}

[Fact]
public void Option_result_provides_identifier_token_if_name_was_provided()
{
Expand Down
21 changes: 18 additions & 3 deletions src/System.CommandLine/ArgumentValidation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.CommandLine.Parsing;
using System.IO;
using System.Linq;

namespace System.CommandLine
{
Expand Down Expand Up @@ -131,7 +132,21 @@ public static Argument<T> AcceptOnlyFromAmong<T>(
this Argument<T> argument,
params string[] values)
{
if (values is not null && values.Length > 0)
return AcceptOnlyFromAmong(argument, StringComparer.Ordinal, values);
}

/// <summary>
/// Configures the argument to accept only the specified values using the specified comparer, and to suggest them as command line completions.
/// </summary>
/// <param name="argument">The argument to configure.</param>
/// <param name="comparer">The comparer used to match argument values against the allowed values.</param>
/// <param name="values">The values that are allowed for the argument.</param>
public static Argument<T> AcceptOnlyFromAmong<T>(
this Argument<T> argument,
StringComparer comparer,
params string[] values)
{
if (values?.Length > 0)
{
argument.Validators.Clear();
argument.Validators.Add(UnrecognizedArgumentError);
Expand All @@ -140,7 +155,7 @@ public static Argument<T> AcceptOnlyFromAmong<T>(
}

return argument;

void UnrecognizedArgumentError(ArgumentResult argumentResult)
{
for (var i = 0; i < argumentResult.Tokens.Count; i++)
Expand All @@ -149,7 +164,7 @@ void UnrecognizedArgumentError(ArgumentResult argumentResult)

if (token.Symbol is null || token.Symbol == argument)
{
if (Array.IndexOf(values, token.Value) < 0)
if (!values.Contains(token.Value, comparer))
{
argumentResult.AddError(LocalizationResources.UnrecognizedArgument(token.Value, values));
}
Expand Down
11 changes: 11 additions & 0 deletions src/System.CommandLine/Option{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ public Option<T> AcceptOnlyFromAmong(params string[] values)
return this;
}

/// <summary>
/// Configures the option to accept only the specified values using the specified comparer, and to suggest them as command line completions.
/// </summary>
/// <param name="comparer">The comparer used to match argument values against the allowed values.</param>
/// <param name="values">The values that are allowed for the option.</param>
public Option<T> AcceptOnlyFromAmong(StringComparer comparer, params string[] values)
{
_argument.AcceptOnlyFromAmong(comparer, values);
return this;
}

/// <summary>
/// Configures the option to accept only values representing legal file paths.
/// </summary>
Expand Down