Open
Description
Background and motivation
In #68578 (comment), we have introduced a set of symbol types for building a parsable hierarchy.
In #84177 (comment) a set of parsing APIs that expose information required by validators.
In this issue, we want to propose a set of APIs for validation.
API Proposal
namespace System.CommandLine;
public abstract class CliArgument : CliSymbol
{
/// <summary>
/// Provides a list of argument validators. Validators can be used
/// to provide custom errors based on user input.
/// </summary>
public List<Action<ArgumentResult>> Validators { get; }
/// <summary>
/// Configures the argument to accept only the specified values, and to suggest them as command line completions.
/// </summary>
/// <param name="values">The values that are allowed for the argument.</param>
public void AcceptOnlyFromAmong(params string[] values);
/// <summary>
/// Configures the argument to accept only values representing legal file paths.
/// </summary>
public void AcceptLegalFilePathsOnly();
/// <summary>
/// Configures the argument to accept only values representing legal file names.
/// </summary>
/// <remarks>A parse error will result, for example, if file path separators are found in the parsed value.</remarks>
public void AcceptLegalFileNamesOnly();
}
/// <summary>
/// Provides extension methods for <see cref="CliArgument" />.
/// </summary>
public static class ArgumentValidation
{
/// <summary>
/// Configures an argument to accept only values corresponding to an existing file.
/// </summary>
/// <param name="argument">The argument to configure.</param>
/// <returns>The configured argument.</returns>
public static CliArgument<FileInfo> AcceptExistingOnly(this CliArgument<FileInfo> argument);
public static CliArgument<DirectoryInfo> AcceptExistingOnly(this CliArgument<DirectoryInfo> argument);
public static CliArgument<FileSystemInfo> AcceptExistingOnly(this CliArgument<FileSystemInfo> argument);
public static CliArgument<T> AcceptExistingOnly<T>(this CliArgument<T> argument)
where T : IEnumerable<FileSystemInfo>
}
public abstract class CliOption : CliSymbol
{
/// <summary>
/// Validators that will be called when the option is matched by the parser.
/// </summary>
public List<Action<OptionResult>> Validators { get; }
/// <summary>
/// Configures the option to accept only the specified values, and to suggest them as command line completions.
/// </summary>
/// <param name="values">The values that are allowed for the option.</param>
public void AcceptOnlyFromAmong(params string[] values);
/// <summary>
/// Configures the option to accept only values representing legal file paths.
/// </summary>
public void AcceptLegalFilePathsOnly();
/// <summary>
/// Configures the option to accept only values representing legal file names.
/// </summary>
/// <remarks>A parse error will result, for example, if file path separators are found in the parsed value.</remarks>
public void AcceptLegalFileNamesOnly();
}
/// <summary>
/// Provides extension methods for <see cref="CliOption" />.
/// </summary>
public static class OptionValidation
{
public static CliOption<FileInfo> AcceptExistingOnly(this CliOption<FileInfo> option);
public static CliOption<DirectoryInfo> AcceptExistingOnly(this CliOption<DirectoryInfo> option);
public static CliOption<FileSystemInfo> AcceptExistingOnly(this CliOption<FileSystemInfo> option);
public static CliOption<T> AcceptExistingOnly<T>(this CliOption<T> option)
where T : IEnumerable<FileSystemInfo>
}
public class CliCommand : CliSymbol, IEnumerable<CliSymbol>
{
/// <summary>
/// Validators to the command. Validators can be used
/// to create custom validation logic.
/// </summary>
public List<Action<CommandResult>> Validators { get; }
}
API Usage
Defining a custom validator for option:
CliOption<int> option = new ("-x");
option.Validators.Add(result =>
{
int value = result.GetValue(option);
if (value < 0 || value > 100)
{
result.AddError("The value of option '-x' must be between 1 and 100.");
}
});
Using built-in validator to ensure that the input file exists:
CliCommand command = new ("move")
{
new CliArgument<FileInfo>("from").AcceptExistingOnly(),
new CliArgument<FileInfo>("to")
};
Risks
AcceptLegalFilePathsOnly
and AcceptLegalFileNamesOnly
are not constrained to any specific T, while they make sense only for string
, FileInfo
, DirectoryInfo
, FileSystemInfo
and types implementing IEnumerable<T>
for those types.