Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.CommandLine: Validation #84178

Open
adamsitnik opened this issue Mar 31, 2023 · 1 comment
Open

System.CommandLine: Validation #84178

adamsitnik opened this issue Mar 31, 2023 · 1 comment
Assignees
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-System.Console
Milestone

Comments

@adamsitnik
Copy link
Member

adamsitnik commented Mar 31, 2023

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.

@adamsitnik adamsitnik added area-System.Console api-ready-for-review API is ready for review, it is NOT ready for implementation labels Mar 31, 2023
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Mar 31, 2023
@ghost
Copy link

ghost commented Mar 31, 2023

Tagging subscribers to this area: @dotnet/area-system-console
See info in area-owners.md if you want to be subscribed.

Issue Details

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.

Author: adamsitnik
Assignees: -
Labels:

area-System.Console, api-ready-for-review

Milestone: -

@adamsitnik adamsitnik removed the untriaged New issue has not been triaged by the area owner label Mar 31, 2023
@adamsitnik adamsitnik self-assigned this Mar 31, 2023
@adamsitnik adamsitnik added this to the 8.0.0 milestone Apr 3, 2023
@adamsitnik adamsitnik added api-needs-work API needs work before it is approved, it is NOT ready for implementation and removed api-ready-for-review API is ready for review, it is NOT ready for implementation labels May 9, 2023
@adamsitnik adamsitnik modified the milestones: 8.0.0, 9.0.0 Jul 27, 2023
@adamsitnik adamsitnik modified the milestones: 9.0.0, Future Jul 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-System.Console
Projects
None yet
Development

No branches or pull requests

3 participants