Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions src/System.Management.Automation/engine/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,17 @@ public sealed class ValidatePatternAttribute : ValidateEnumeratedArgumentsAttrib
/// </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>
Expand All @@ -1136,8 +1147,9 @@ protected override void ValidateElement(object element)
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);
}
}
Expand All @@ -1163,6 +1175,18 @@ public ValidatePatternAttribute(string regexPattern)
/// </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>
Expand Down Expand Up @@ -1193,8 +1217,9 @@ protected override void ValidateElement(object element)

if (!LanguagePrimitives.IsTrue(result))
{
var errorMessageFormat = String.IsNullOrEmpty(ErrorMessage) ? Metadata.ValidateScriptFailure : ErrorMessage;
throw new ValidationMetadataException("ValidateScriptFailure",
null, Metadata.ValidateScriptFailure,
null, errorMessageFormat,
element, ScriptBlock);
}
}
Expand Down Expand Up @@ -1336,6 +1361,18 @@ public sealed class ValidateSetAttribute : ValidateEnumeratedArgumentsAttribute
{
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.
Expand Down Expand Up @@ -1386,8 +1423,10 @@ protected override void ValidateElement(object element)
return;
}
}

var errorMessageFormat = String.IsNullOrEmpty(ErrorMessage) ? Metadata.ValidateSetFailure : ErrorMessage;
throw new ValidationMetadataException("ValidateSetFailure", null,
Metadata.ValidateSetFailure,
errorMessageFormat,
element.ToString(), SetAsString());
}

Expand Down
4 changes: 4 additions & 0 deletions src/System.Management.Automation/engine/parser/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,10 @@ private static Attribute NewValidateSetAttribute(AttributeAst ast)
{
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,
Expand Down
56 changes: 56 additions & 0 deletions test/powershell/Language/Scripting/ParameterBinding.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,62 @@ Describe "Tests for parameter binding" -Tags "CI" {

{ 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
Expand Down