Add ErrorMessage property to ValidatePattern, ValidateSet and ValidateScript attributes (#2728)

This makes it possibe to write for example
[ValidatePattern('[A-Z]:',  ErrorMessage='The Drive should be specified as a single letter followed by a colon, for example "D:"')]
[string] $Drive,

The element being validated is also passed, so {0} can be used in the custom error message
This commit is contained in:
Staffan Gustafsson 2017-04-20 20:31:57 +02:00 committed by Jason Shirk
parent e8a0f3ca63
commit fa901b646f
3 changed files with 102 additions and 3 deletions

View file

@ -1113,6 +1113,17 @@ namespace System.Management.Automation
/// </summary>
public RegexOptions Options { set; get; } = RegexOptions.IgnoreCase;
/// <summary>
/// Gets or sets the custom error message pattern that is displayed to the user.
///
/// The text representation of the object being validated and the validating regex is passed as
/// the first and second formatting parameters to the ErrorMessage formatting pattern.
/// <example>
/// [ValidatePattern("\s+", ErrorMessage="The text '{0}' did not pass validation of regex '{1}'")]
/// </example>
/// </summary>
public string ErrorMessage { get; set; }
/// <summary>
/// Validates that each parameter argument matches the RegexPattern
/// </summary>
@ -1136,8 +1147,9 @@ namespace System.Management.Automation
Match match = regex.Match(objectString);
if (!match.Success)
{
var errorMessageFormat = String.IsNullOrEmpty(ErrorMessage) ? Metadata.ValidatePatternFailure : ErrorMessage;
throw new ValidationMetadataException("ValidatePatternFailure",
null, Metadata.ValidatePatternFailure,
null, errorMessageFormat,
objectString, RegexPattern);
}
}
@ -1163,6 +1175,18 @@ namespace System.Management.Automation
/// </summary>
public sealed class ValidateScriptAttribute : ValidateEnumeratedArgumentsAttribute
{
/// <summary>
/// Gets or sets the custom error message that is displayed to the user.
///
/// The item being validated and the validating scriptblock is passed as the first and second
/// formatting argument.
///
/// <example>
/// [ValidateScript("$_ % 2", ErrorMessage = "The item '{0}' did not pass validation of script '{1}'")]
/// </example>
/// </summary>
public string ErrorMessage { get; set; }
/// <summary>
/// Gets the scriptblock to be used in the validation
/// </summary>
@ -1193,8 +1217,9 @@ namespace System.Management.Automation
if (!LanguagePrimitives.IsTrue(result))
{
var errorMessageFormat = String.IsNullOrEmpty(ErrorMessage) ? Metadata.ValidateScriptFailure : ErrorMessage;
throw new ValidationMetadataException("ValidateScriptFailure",
null, Metadata.ValidateScriptFailure,
null, errorMessageFormat,
element, ScriptBlock);
}
}
@ -1336,6 +1361,18 @@ namespace System.Management.Automation
{
private string[] _validValues;
/// <summary>
/// Gets or sets the custom error message that is displayed to the user
///
/// The item being validated and a text representation of the validation set
/// is passed as the first and second formatting argument to the ErrorMessage formatting pattern.
///
/// <example>
/// [ValidateSet("A","B","C", ErrorMessage="The item '{0}' is not part of the set '{1}'.")
/// </example>
/// </summary>
public string ErrorMessage { get; set; }
/// <summary>
/// Gets a flag specifying if we should ignore the case when performing string comparison. The
/// default is true.
@ -1386,8 +1423,10 @@ namespace System.Management.Automation
return;
}
}
var errorMessageFormat = String.IsNullOrEmpty(ErrorMessage) ? Metadata.ValidateSetFailure : ErrorMessage;
throw new ValidationMetadataException("ValidateSetFailure", null,
Metadata.ValidateSetFailure,
errorMessageFormat,
element.ToString(), SetAsString());
}

View file

@ -1313,6 +1313,10 @@ namespace System.Management.Automation.Language
{
result.IgnoreCase = s_attrArgToBoolConverter.Target(s_attrArgToBoolConverter, argValue);
}
else if (argumentName.Equals("ErrorMessage", StringComparison.OrdinalIgnoreCase))
{
result.ErrorMessage = argValue.ToString();
}
else
{
throw InterpreterError.NewInterpreterException(namedArg, typeof(RuntimeException), namedArg.Extent,

View file

@ -412,6 +412,62 @@
{ get-fooh } | Should not throw
}
It "ValidateScript can use custom ErrorMessage" {
function get-fooi {
[CmdletBinding()]
param([ValidateScript({$_ -gt 2}, ErrorMessage = "Item '{0}' failed '{1}' validation")] $p)
$p
}
$errMsg = ''
try
{
get-fooi -p 2
}
catch
{
$errMsg = $_.Exception.Message
}
$errMsg | Should Be "Cannot validate argument on parameter 'p'. Item '2' failed '`$_ -gt 2' validation"
}
It "ValidatePattern can use custom ErrorMessage" {
function get-fooj
{
[CmdletBinding()]
param([ValidatePattern("\s+", ErrorMessage = "Item '{0}' failed '{1}' regex")] $p)
$p
}
$errMsg = ''
try
{
get-fooj -p 2
}
catch
{
$errMsg = $_.Exception.Message
}
$errMsg | Should Be "Cannot validate argument on parameter 'p'. Item '2' failed '\s+' regex"
}
It "ValidateSet can use custom ErrorMessage" {
function get-fook
{
param([ValidateSet('A', 'B', 'C', IgnoreCase=$false, ErrorMessage="Item '{0}' is not in '{1}'")] $p)
}
$errMsg = ''
try
{
get-fook -p 2
}
catch
{
$errMsg = $_.Exception.Message
}
$set = 'A','B','C' -join [Globalization.CultureInfo]::CurrentUICulture.TextInfo.ListSeparator
$errMsg | Should Be "Cannot validate argument on parameter 'p'. Item '2' is not in '$set'"
}
}
#known issue 2069