| File: ArgumentValidation.cs | Web Access |
| Project: src\command-line-api\src\System.CommandLine\System.CommandLine.csproj (System.CommandLine) |
// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Generic; using System.CommandLine.Parsing; using System.IO; using System.Linq; namespace System.CommandLine { /// <summary> /// Provides extension methods for <see cref="Argument" />. /// </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 Argument<FileInfo> AcceptExistingOnly(this Argument<FileInfo> argument) { argument.Validators.Add(FileOrDirectoryExists<FileInfo>); return argument; } /// <summary> /// Configures an argument to accept only values corresponding to an existing directory. /// </summary> /// <param name="argument">The argument to configure.</param> /// <returns>The configured argument.</returns> public static Argument<DirectoryInfo> AcceptExistingOnly(this Argument<DirectoryInfo> argument) { argument.Validators.Add(FileOrDirectoryExists<DirectoryInfo>); return argument; } /// <summary> /// Configures an argument to accept only values corresponding to an existing file or directory. /// </summary> /// <param name="argument">The argument to configure.</param> /// <returns>The configured argument.</returns> public static Argument<FileSystemInfo> AcceptExistingOnly(this Argument<FileSystemInfo> argument) { argument.Validators.Add(FileOrDirectoryExists<FileSystemInfo>); return argument; } /// <summary> /// Configures an argument to accept only values corresponding to a existing files or directories. /// </summary> /// <param name="argument">The argument to configure.</param> /// <returns>The configured argument.</returns> public static Argument<T> AcceptExistingOnly<T>(this Argument<T> argument) where T : IEnumerable<FileSystemInfo> { if (typeof(IEnumerable<FileInfo>).IsAssignableFrom(typeof(T))) { argument.Validators.Add(FileOrDirectoryExists<FileInfo>); } else if (typeof(IEnumerable<DirectoryInfo>).IsAssignableFrom(typeof(T))) { argument.Validators.Add(FileOrDirectoryExists<DirectoryInfo>); } else { argument.Validators.Add(FileOrDirectoryExists<FileSystemInfo>); } return argument; } /// <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 static Argument<T> AcceptLegalFileNamesOnly<T>(this Argument<T> argument) { argument.Validators.Add(static result => { var invalidFileNameChars = Path.GetInvalidFileNameChars(); for (var i = 0; i < result.Tokens.Count; i++) { var token = result.Tokens[i]; var invalidCharactersIndex = token.Value.IndexOfAny(invalidFileNameChars); if (invalidCharactersIndex >= 0) { result.AddError(LocalizationResources.InvalidCharactersInFileName(token.Value[invalidCharactersIndex])); } } }); return argument; } /// <summary> /// Configures the argument to accept only values representing legal file paths. /// </summary> public static Argument<T> AcceptLegalFilePathsOnly<T>(this Argument<T> argument) { argument.Validators.Add(static result => { var invalidPathChars = Path.GetInvalidPathChars(); for (var i = 0; i < result.Tokens.Count; i++) { var token = result.Tokens[i]; // File class no longer check invalid character // https://blogs.msdn.microsoft.com/jeremykuhne/2018/03/09/custom-directory-enumeration-in-net-core-2-1/ var invalidCharactersIndex = token.Value.IndexOfAny(invalidPathChars); if (invalidCharactersIndex >= 0) { result.AddError(LocalizationResources.InvalidCharactersInPath(token.Value[invalidCharactersIndex])); } } }); return argument; } /// <summary> /// Configures the argument to accept only the specified values, and to suggest them as command line completions. /// </summary> /// <param name="argument">The argument to configure.</param> /// <param name="values">The values that are allowed for the argument.</param> public static Argument<T> AcceptOnlyFromAmong<T>( this Argument<T> argument, params string[] values) { 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); argument.CompletionSources.Clear(); argument.CompletionSources.Add(values); } return argument; void UnrecognizedArgumentError(ArgumentResult argumentResult) { for (var i = 0; i < argumentResult.Tokens.Count; i++) { var token = argumentResult.Tokens[i]; if (token.Symbol is null || token.Symbol == argument) { if (!values.Contains(token.Value, comparer)) { argumentResult.AddError(LocalizationResources.UnrecognizedArgument(token.Value, values)); } } } } } private static void FileOrDirectoryExists<T>(ArgumentResult result) where T : FileSystemInfo { // both FileInfo and DirectoryInfo are sealed so following checks are enough bool checkFile = typeof(T) != typeof(DirectoryInfo); bool checkDirectory = typeof(T) != typeof(FileInfo); for (var i = 0; i < result.Tokens.Count; i++) { var token = result.Tokens[i]; if (checkFile && checkDirectory) { #if NET7_0_OR_GREATER if (!Path.Exists(token.Value)) #else if (!Directory.Exists(token.Value) && !File.Exists(token.Value)) #endif { result.AddError(LocalizationResources.FileOrDirectoryDoesNotExist(token.Value)); } } else if (checkDirectory && !Directory.Exists(token.Value)) { result.AddError(LocalizationResources.DirectoryDoesNotExist(token.Value)); } else if (checkFile && !Directory.Exists(token.Value) && !File.Exists(token.Value)) { result.AddError(LocalizationResources.FileDoesNotExist(token.Value)); } } } } }