2

I am attempting to use ValidateSet with a boolean parameter however I cannot get it to function as expect.

An example to replicate the issue:

function Set-Boolean
{
    [CmdletBinding()]
    [OutputType([Bool])]
    Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet($false,$true)]
        [Bool] $Bool
    )

    Process
    {
        $Bool
    }
}

At run time PowerShell expands the $true and $false variables upon tab completion to True and False respectively however this then cause the parameter validation to fail as only $true, $false, 1 and 0 are valid boolean types.

I tried changing ValidateSet to [ValidateSet('$false','$true')] and the tab completion works as expected however the parameter validation still fails as the parameter is expecting the strings '$false' and '$true'.

I could change ValidateSet to [ValidateSet([bool]0,[bool]1)] and have it function as I expect but I find 0 and 1 a poorer user experience over $true and $false for completion of a boolean parameter.

The expected output of the function should be True when $true is chosen and False when $false is chosen.

I have implemented a workaround in my code but I want to know how I can use ValidateSet with a boolean parameter, if it is possible, to enable the use of tab completion for the user.

Persistent13
  • 1,522
  • 11
  • 20
  • 1
    Have you considered using a `[switch]` parameter instead? – Mark Wragg Jul 12 '17 at 06:00
  • Thanks for pointing out the switch parameter Mark. I have considered it, but for the problem I'm facing I believe it would lead to a poorer UX. The API my function uses requires an explicit $true or $false to enable or disable it's activity. Using a switch would necessitate an -Enable and a -Disable parameter if I chose to use switches rather than a single Participate parameter that I am using now. – Persistent13 Jul 12 '17 at 22:59
  • 1
    I believe the switch parameter should evaluate to $true or $false depending on whether or not it's invoked, so you shouldn't need two. – Mark Wragg Jul 12 '17 at 23:02

3 Answers3

3

Joining some answers, the best option will depend on the needs, if the parameter is type boolean it can be replaced by one of type switch, but if you want to specify the value to see in the code or by style; It can be done as follows.

function Write-SwitchParam {
    [CmdletBinding()]
    param (
        [Switch] #[Switch] or [System.Management.Automation.SwitchParameter]
        $SwitchParam
    )

    if($SwitchParam)
    {
        Write-Host "Switch is present. Do Positive Action."
    }
    else {
        Write-Host "Switch isn't present. Do Negative Action."
    }
}

function Write-BooleanParam {
    [CmdletBinding()]
    Param(
    [parameter(Position=0, ValueFromPipeline=$true)]
    [ValidateNotNullOrEmpty()]
    [ValidateSet($true, $false, 0, 1)]
    $BoolParam = $true
    )

    Begin{  
    }

    Process{
        $value = [System.Convert]::ToBoolean($BoolParam)
        Write-Host $value
    }

    End{
    }   
}

Write-Host "TESTS BOOLEAN PARAM" -ForegroundColor Magenta
Write-BooleanParam -BoolParam $true
Write-BooleanParam -BoolParam $false
Write-Host

Write-BooleanParam $false
Write-BooleanParam $true
Write-Host

Write-BooleanParam True
Write-BooleanParam False
Write-Host

Write-BooleanParam 0
Write-BooleanParam 1
Write-Host

Write-BooleanParam
Write-Host

$true, $false, "True", "False", 1, 0 | Write-BooleanParam
Write-Host

Write-Host "TESTS SWITCH PARAM" -ForegroundColor Magenta
Write-SwitchParam 
Write-SwitchParam -SwitchParam

Console Output

Joma
  • 3,520
  • 1
  • 29
  • 32
2

You can use ValidateSet for autocompletion without specifying the input variable type. Though, you cannot use 0 and 1 as input in this case:

function Set-Boolean
{
    [CmdletBinding()]
    [OutputType([Bool])]
    Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet($false,$true)]
        $Bool
    )

    Process
    {
        [System.Convert]::ToBoolean($Bool)
    }
}
Mikhail Tumashenko
  • 1,683
  • 2
  • 21
  • 28
  • By cat do you mean can? I was hoping to explicitly type the $Bool variable in order to avoid additional parameter validation with in the script. – Persistent13 Jul 12 '17 at 23:02
  • You validate the parameter only with the ValidateSet. There's no validation within the script, just conversion. And now the script accepts string and Boolean versions of True and False, but only them. – Mikhail Tumashenko Jul 13 '17 at 03:48
0

This gives the tab completion for the user, as desired using ValidateSet, but is achieved by registering an argument completer instead (especially in the ISE);

function Set-Boolean
{
    [CmdletBinding()]
    [OutputType([Bool])]
    Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [ValidateNotNullOrEmpty()]
        [Bool] $Bool
    )

    Process
    {
        $Bool
    }
}
Register-ArgumentCompleter -CommandName 'Set-Boolean' -ParameterName 'Bool' -ScriptBlock {
    [System.Management.Automation.CompletionResult]::new(
        '$False',
        '$False',
        'ParameterValue',
        '[bool] False'
    )
    [System.Management.Automation.CompletionResult]::new(
        '$True',
        '$True',
        'ParameterValue',
        '[bool] True'
    )
}
Steve_N
  • 1
  • 1
  • If you don't want the ISE graphic completion you can use; [System.Management.Automation.CompletionResult]::new( '$False' ) [System.Management.Automation.CompletionResult]::new( '$True' ) – Steve_N Nov 09 '21 at 16:33