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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix trimming runtime error #491

Merged
merged 4 commits into from
Feb 1, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
60 changes: 42 additions & 18 deletions src/CommandLineUtils/Validation/ValidationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ public static class ValidationExtensions
public static CommandOption IsRequired(this CommandOption option, bool allowEmptyStrings = false, string? errorMessage = null)
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
{
var attribute = GetValidationAttr<RequiredAttribute>(errorMessage);
attribute.AllowEmptyStrings = allowEmptyStrings;
var attribute = AddErrorMessage(new RequiredAttribute
{
AllowEmptyStrings = allowEmptyStrings
}, errorMessage);
option.Validators.Add(new AttributeValidator(attribute));
return option;
}
Expand Down Expand Up @@ -57,8 +59,10 @@ public static CommandOption IsRequired(this CommandOption option, bool allowEmpt
public static CommandArgument IsRequired(this CommandArgument argument, bool allowEmptyStrings = false, string? errorMessage = null)
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
{
var attribute = GetValidationAttr<RequiredAttribute>(errorMessage);
attribute.AllowEmptyStrings = allowEmptyStrings;
var attribute = AddErrorMessage(new RequiredAttribute
{
AllowEmptyStrings = allowEmptyStrings
}, errorMessage);
argument.Validators.Add(new AttributeValidator(attribute));
return argument;
}
Expand Down Expand Up @@ -247,7 +251,7 @@ public static IValidationBuilder Values(this IValidationBuilder builder, bool ig
/// <returns>The builder.</returns>
public static IValidationBuilder Values(this IValidationBuilder builder, StringComparison comparer, params string[] allowedValues)
{
return builder.Satisfies<AllowedValuesAttribute>(ctorArgs: new object[] { comparer, allowedValues });
return builder.Satisfies(new AllowedValuesAttribute(comparer, allowedValues));
}

/// <summary>
Expand All @@ -257,7 +261,7 @@ public static IValidationBuilder Values(this IValidationBuilder builder, StringC
/// <param name="errorMessage">A custom error message to display.</param>
/// <returns>The builder.</returns>
public static IValidationBuilder EmailAddress(this IValidationBuilder builder, string? errorMessage = null)
=> builder.Satisfies<EmailAddressAttribute>(errorMessage);
=> builder.Satisfies(new EmailAddressAttribute(), errorMessage);

/// <summary>
/// Specifies that values must be a path to a file that already exists.
Expand All @@ -266,7 +270,7 @@ public static IValidationBuilder EmailAddress(this IValidationBuilder builder, s
/// <param name="errorMessage">A custom error message to display.</param>
/// <returns>The builder.</returns>
public static IValidationBuilder ExistingFile(this IValidationBuilder builder, string? errorMessage = null)
=> builder.Satisfies<FileExistsAttribute>(errorMessage);
=> builder.Satisfies(new FileExistsAttribute(), errorMessage);

/// <summary>
/// Specifies that values must be a path to a file that does not already exist.
Expand All @@ -275,7 +279,7 @@ public static IValidationBuilder ExistingFile(this IValidationBuilder builder, s
/// <param name="errorMessage">A custom error message to display.</param>
/// <returns>The builder.</returns>
public static IValidationBuilder NonExistingFile(this IValidationBuilder builder, string? errorMessage = null)
=> builder.Satisfies<FileNotExistsAttribute>(errorMessage);
=> builder.Satisfies(new FileNotExistsAttribute(), errorMessage);

/// <summary>
/// Specifies that values must be a path to a directory that already exists.
Expand All @@ -284,7 +288,7 @@ public static IValidationBuilder NonExistingFile(this IValidationBuilder builder
/// <param name="errorMessage">A custom error message to display.</param>
/// <returns>The builder.</returns>
public static IValidationBuilder ExistingDirectory(this IValidationBuilder builder, string? errorMessage = null)
=> builder.Satisfies<DirectoryExistsAttribute>(errorMessage);
=> builder.Satisfies(new DirectoryExistsAttribute(), errorMessage);

/// <summary>
/// Specifies that values must be a path to a directory that does not already exist.
Expand All @@ -293,7 +297,7 @@ public static IValidationBuilder ExistingDirectory(this IValidationBuilder build
/// <param name="errorMessage">A custom error message to display.</param>
/// <returns>The builder.</returns>
public static IValidationBuilder NonExistingDirectory(this IValidationBuilder builder, string? errorMessage = null)
=> builder.Satisfies<DirectoryNotExistsAttribute>(errorMessage);
=> builder.Satisfies(new DirectoryNotExistsAttribute(), errorMessage);

/// <summary>
/// Specifies that values must be a valid file path or directory, and the file path must already exist.
Expand All @@ -302,7 +306,7 @@ public static IValidationBuilder NonExistingDirectory(this IValidationBuilder bu
/// <param name="errorMessage">A custom error message to display.</param>
/// <returns>The builder.</returns>
public static IValidationBuilder ExistingFileOrDirectory(this IValidationBuilder builder, string? errorMessage = null)
=> builder.Satisfies<FileOrDirectoryExistsAttribute>(errorMessage);
=> builder.Satisfies(new FileOrDirectoryExistsAttribute(), errorMessage);

/// <summary>
/// Specifies that values must be a valid file path or directory, and the file path must not already exist.
Expand All @@ -311,7 +315,7 @@ public static IValidationBuilder ExistingFileOrDirectory(this IValidationBuilder
/// <param name="errorMessage">A custom error message to display.</param>
/// <returns>The builder.</returns>
public static IValidationBuilder NonExistingFileOrDirectory(this IValidationBuilder builder, string? errorMessage = null)
=> builder.Satisfies<FileOrDirectoryNotExistsAttribute>(errorMessage);
=> builder.Satisfies(new FileOrDirectoryNotExistsAttribute(), errorMessage);

/// <summary>
/// Specifies that values must be legal file paths.
Expand All @@ -320,7 +324,7 @@ public static IValidationBuilder NonExistingFileOrDirectory(this IValidationBuil
/// <param name="errorMessage">A custom error message to display.</param>
/// <returns>The builder.</returns>
public static IValidationBuilder LegalFilePath(this IValidationBuilder builder, string? errorMessage = null)
=> builder.Satisfies<LegalFilePathAttribute>(errorMessage);
=> builder.Satisfies(new LegalFilePathAttribute(), errorMessage);

/// <summary>
/// Specifies that values must be a string at least <paramref name="length"/> characters long.
Expand All @@ -330,7 +334,7 @@ public static IValidationBuilder LegalFilePath(this IValidationBuilder builder,
/// <param name="errorMessage">A custom error message to display.</param>
/// <returns>The builder.</returns>
public static IValidationBuilder MinLength(this IValidationBuilder builder, int length, string? errorMessage = null)
=> builder.Satisfies<MinLengthAttribute>(errorMessage, length);
=> builder.Satisfies(new MinLengthAttribute(length), errorMessage);

/// <summary>
/// Specifies that values must be a string no more than <paramref name="length"/> characters long.
Expand All @@ -340,7 +344,7 @@ public static IValidationBuilder MinLength(this IValidationBuilder builder, int
/// <param name="errorMessage">A custom error message to display.</param>
/// <returns>The builder.</returns>
public static IValidationBuilder MaxLength(this IValidationBuilder builder, int length, string? errorMessage = null)
=> builder.Satisfies<MaxLengthAttribute>(errorMessage, length);
=> builder.Satisfies(new MaxLengthAttribute(length), errorMessage);

/// <summary>
/// Specifies that values must match a regular expression.
Expand All @@ -350,7 +354,7 @@ public static IValidationBuilder MaxLength(this IValidationBuilder builder, int
/// <param name="errorMessage">A custom error message to display.</param>
/// <returns>The builder.</returns>
public static IValidationBuilder RegularExpression(this IValidationBuilder builder, string pattern, string? errorMessage = null)
=> builder.Satisfies<RegularExpressionAttribute>(errorMessage, pattern);
=> builder.Satisfies(new RegularExpressionAttribute(pattern), errorMessage);

/// <summary>
/// Specifies that values must satisfy the requirements of the validation attribute of type <typeparamref name="TAttribute"/>.
Expand Down Expand Up @@ -380,7 +384,7 @@ public static IValidationBuilder Satisfies<TAttribute>(this IValidationBuilder b
public static IValidationBuilder<int> Range(this IValidationBuilder<int> builder, int minimum, int maximum, string? errorMessage = null)
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
{
var attribute = GetValidationAttr<RangeAttribute>(errorMessage, new object[] { minimum, maximum });
var attribute = AddErrorMessage(new RangeAttribute(minimum, maximum), errorMessage);
builder.Use(new AttributeValidator(attribute));
return builder;
}
Expand All @@ -397,7 +401,7 @@ public static IValidationBuilder<int> Range(this IValidationBuilder<int> builder
public static IValidationBuilder<double> Range(this IValidationBuilder<double> builder, double minimum, double maximum, string? errorMessage = null)
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
{
var attribute = GetValidationAttr<RangeAttribute>(errorMessage, new object[] { minimum, maximum });
var attribute = AddErrorMessage(new RangeAttribute(minimum, maximum), errorMessage);
builder.Use(new AttributeValidator(attribute));
return builder;
}
Expand Down Expand Up @@ -438,6 +442,16 @@ public static CommandOption OnValidate(this CommandOption option, Func<Validatio
return option;
}

private static IValidationBuilder Satisfies(this IValidationBuilder builder, ValidationAttribute attribute, string? errorMessage = null)
{
if (errorMessage is not null)
{
attribute.ErrorMessage = errorMessage;
}
builder.Use(new AttributeValidator(attribute));
return builder;
}

private static T GetValidationAttr<T>(string? errorMessage, object[]? ctorArgs = null)
where T : ValidationAttribute
{
Expand All @@ -448,5 +462,15 @@ private static T GetValidationAttr<T>(string? errorMessage, object[]? ctorArgs =
}
return attribute;
}

private static T AddErrorMessage<T>(T attribute, string? errorMessage)
where T : ValidationAttribute
{
if (errorMessage != null)
{
attribute.ErrorMessage = errorMessage;
}
return attribute;
}
}
}
19 changes: 18 additions & 1 deletion test/CommandLineUtils.Tests/AttributeValidatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public OptionBuilderApp(TestConsole testConsole)
: base(testConsole)
{
Option("-e|--email", "Email", CommandOptionType.SingleValue)
.Accepts().EmailAddress();
.Accepts().EmailAddress("Invalid Email");

Option("-n|--name", "Name", CommandOptionType.SingleValue)
.Accepts().MinLength(1);
Expand All @@ -115,6 +115,9 @@ public OptionBuilderApp(TestConsole testConsole)

Option("-r|--regex", "Regex", CommandOptionType.SingleValue)
.Accepts().RegularExpression("^abc.*");

Option("-m|--mode", "Mode", CommandOptionType.SingleValue)
.Accepts().Satisfies<ModeValidationAttribute>("With an error message from model validation");
}
}

Expand All @@ -132,6 +135,9 @@ private class OptionApp
[Option, RegularExpression("^abc.*")]
public string? Regex { get; }

[Option, ModeValidation]
public string? Mode { get; }

private void OnExecute() { }
}

Expand All @@ -149,6 +155,8 @@ private class OptionApp
[InlineData(new[] { "-a", "abcdefghijk" }, 1)]
[InlineData(new[] { "-r", "abcdefghijk" }, 0)]
[InlineData(new[] { "-r", "xyz" }, 1)]
[InlineData(new[] { "-m", "xyz" }, 1)]
[InlineData(new[] { "-m", "mode" }, 0)]
public void ValidatesAttributesOnOption(string[] args, int exitCode)
{
Assert.Equal(exitCode, CommandLineApplication.Execute<OptionApp>(new TestConsole(_output), args));
Expand Down Expand Up @@ -179,5 +187,14 @@ public override bool IsValid(object value)
&& app.Arg1 != null && app.Arg1.Contains("good")
&& app.Arg2 != null && app.Arg2.Contains("good");
}

[AttributeUsage(AttributeTargets.Property)]
private sealed class ModeValidationAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
return value is string text && text.Contains("mode");
}
}
}
}