private/uitool/Invoke-UiToolValidation.ps1

<#
.SYNOPSIS
    Validates parameter values for New-UiTool before command execution.
#>

function Invoke-UiToolValidation {
    [CmdletBinding()]
    param(
        [string]$CommandName,
        [string]$ParameterSetName
    )

    $session = Get-UiSession
    $def = $session.PSBase.CurrentDefinition

    if (!$CommandName -and $def) {
        $CommandName = $def.CommandName
    }
    if (!$ParameterSetName -and $def) {
        $ParameterSetName = $def.ParameterSetName
    }

    # Get current parameter set from UI (if selector exists)
    $proxy = [PsUi.SessionManager]::Current.GetSafeVariable('selectedParameterSet')
    $currentSet = if ($proxy) { $proxy.Value } else { $null }
    if (!$currentSet) { $currentSet = $ParameterSetName }

    # Try to get command info - may fail for local functions
    $cmdInfo     = $null
    $cmdLookup   = $CommandName
    $storedParams = $null

    try {
        $cmdInfo = Get-Command $cmdLookup -ErrorAction Stop
    }
    catch {
        # Local function not available globally - use stored param info from session
        $session      = Get-UiSession
        $storedParams = $session.Variables['_uiTool_paramInfo']
    }

    $commonParams = @(
        'Verbose','Debug','ErrorAction','WarningAction','InformationAction',
        'ErrorVariable','WarningVariable','InformationVariable',
        'OutVariable','OutBuffer','PipelineVariable','WhatIf','Confirm','UseTransaction'
    )

    # Build current params list from cmdInfo or stored params
    $currentParams = $null
    if ($storedParams) {
        $currentParams = $storedParams
    }
    elseif ($cmdInfo) {
        $paramSetDef   = $cmdInfo.ParameterSets | Where-Object { $_.Name -eq $currentSet }
        $currentParams = [System.Collections.Generic.List[object]]::new()

        foreach ($paramName in $cmdInfo.Parameters.Keys) {
            if ($commonParams -contains $paramName) { continue }

            $param = $cmdInfo.Parameters[$paramName]
            if ($currentSet) {
                $inSet = $param.ParameterSets.ContainsKey($currentSet) -or
                         $param.ParameterSets.ContainsKey('__AllParameterSets')
                if (!$inSet) { continue }
            }

            # Check mandatory for THIS specific parameter set
            $isMandatoryInSet = $false
            if ($paramSetDef) {
                $paramInSet = $paramSetDef.Parameters | Where-Object { $_.Name -eq $paramName }
                if ($paramInSet) { $isMandatoryInSet = $paramInSet.IsMandatory }
            }

            $currentParams.Add([PSCustomObject]@{
                Name        = $paramName
                Type        = $param.ParameterType
                IsMandatory = $isMandatoryInSet
                IsSwitch    = $param.ParameterType -eq [switch]
            })
        }
    }

    # Can't validate without param info - proceed anyway
    if (!$currentParams) { return @() }

    $paramHash        = @{}
    $validationErrors = [System.Collections.Generic.List[string]]::new()
    $session          = Get-UiSession

    foreach ($paramDef in $currentParams) {
        $varName = "param_$($paramDef.Name)"
        $value   = $null

        # PSCredential uses a special wrapper stored in session.Variables
        if ($paramDef.Type -eq [System.Management.Automation.PSCredential]) {
            $credWrapper = $session.Variables[$varName]
            if ($credWrapper -and $credWrapper.PSObject.TypeNames -contains 'PsUi.CredentialControl') {
                # Assemble PSCredential from username + password boxes
                $username = $credWrapper.UsernameBox.Text
                $secPass  = $credWrapper.PasswordBox.SecurePassword
                if (![string]::IsNullOrWhiteSpace($username) -and $secPass.Length -gt 0) {
                    $value = [System.Management.Automation.PSCredential]::new($username, $secPass)
                }
            }
        }
        else {
            # Standard controls use SafeVariables proxy
            $proxy = [PsUi.SessionManager]::Current.GetSafeVariable($varName)
            $value = if ($proxy) { $proxy.Value } else { $null }
        }

        # For PSCredential, check null instead of IsNullOrWhiteSpace
        $isEmpty = if ($paramDef.Type -eq [System.Management.Automation.PSCredential]) {
            $null -eq $value
        }
        else {
            [string]::IsNullOrWhiteSpace($value)
        }

        # Skip empty non-mandatory values
        if ($isEmpty -and !$paramDef.IsMandatory) { continue }

        # Mandatory field left blank
        if ($paramDef.IsMandatory -and $isEmpty -and !$paramDef.IsSwitch) {
            $validationErrors.Add("$($paramDef.Name) is required")
            continue
        }

        try {
            if ($paramDef.IsSwitch) {
                if ($value -eq $true) { $paramHash[$paramDef.Name] = [switch]::Present }
            }
            elseif ($paramDef.Type -eq [string[]]) {
                if (![string]::IsNullOrWhiteSpace($value)) {
                    $paramHash[$paramDef.Name] = $value -split "`r?`n" | Where-Object { $_.Trim() }
                }
            }
            elseif ($paramDef.Type -eq [int] -or $paramDef.Type -eq [int32]) {
                if (![string]::IsNullOrWhiteSpace($value)) {
                    $paramHash[$paramDef.Name] = [int]$value
                }
            }
            elseif ($paramDef.Type -eq [int64]) {
                if (![string]::IsNullOrWhiteSpace($value)) {
                    $paramHash[$paramDef.Name] = [int64]$value
                }
            }
            elseif ($paramDef.Type -eq [double] -or $paramDef.Type -eq [float]) {
                if (![string]::IsNullOrWhiteSpace($value)) {
                    $paramHash[$paramDef.Name] = [double]$value
                }
            }
            elseif ($paramDef.Type -eq [bool]) {
                $paramHash[$paramDef.Name] = $value -eq $true
            }
            elseif ($paramDef.Type -eq [datetime]) {
                if ($value) { $paramHash[$paramDef.Name] = [datetime]$value }
            }
            elseif ($paramDef.Type -eq [System.Security.SecureString]) {
                if (![string]::IsNullOrWhiteSpace($value)) {
                    $paramHash[$paramDef.Name] = ConvertTo-SecureString $value -AsPlainText -Force
                }
            }
            elseif ($paramDef.Type -eq [System.Management.Automation.PSCredential]) {
                if ($value -and $value -is [System.Management.Automation.PSCredential]) {
                    $paramHash[$paramDef.Name] = $value
                }
                elseif ($paramDef.IsMandatory) {
                    $validationErrors.Add("$($paramDef.Name): Credential is required")
                }
            }
            elseif ($paramDef.Type -eq [scriptblock]) {
                if (![string]::IsNullOrWhiteSpace($value)) {
                    $paramHash[$paramDef.Name] = [scriptblock]::Create($value)
                }
            }
            else {
                if (![string]::IsNullOrWhiteSpace($value)) {
                    $paramHash[$paramDef.Name] = $value
                }
            }
        }
        catch {
            $validationErrors.Add("$($paramDef.Name): $($_.Exception.Message)")
        }
    }

    # Store validated params for the action to use
    if ($validationErrors.Count -eq 0) {
        $session = Get-UiSession
        $session.Variables['_uiTool_validatedParams'] = $paramHash
    }

    return $validationErrors
}