# encoding: ascii
# api: powershell
# title: Invoke-Switch
# description: A function to invoke a switch statement, in order to allow splatting to be used with switch
# version: 0.9
# type: function
# author: Kyle A Eppler
# license: CC0
# function: Invoke-Switch
# x-poshcode-id: 3873
# x-archived: 2013-01-22T14:51:03
# x-published: 2013-01-10T16:01:00
#
# Dependencies:
# This function, as written in this version, requires the existence of a repr function such as that defined by http://poshcode.org/3872 and its successors.
# I plan to post a new version of this function that does not limit the objects that may be used as either values to match against, or values to return, to only those that support round-trip serialization and deserialization to a string and back, using a repr function for the serialization. I’m first posting this version in case some technique used in it would be useful elsewhere.
#
New-Variable castDictionaryEntries
[System.Func[System.Collections.IEnumerable, System.Collections.Generic.IEnumerable[System.Collections.DictionaryEntry]]] `
    $castDictionaryEntries = [System.Delegate]::CreateDelegate(
        [System.Func[System.Collections.IEnumerable, System.Collections.Generic.IEnumerable[System.Collections.DictionaryEntry]]],
        [System.Linq.Enumerable].GetMethod(
            'Cast', @('Public, Static')).MakeGenericMethod(
                @([System.Collections.DictionaryEntry])))
Set-Variable castDictionaryEntries -Option ReadOnly

function Invoke-Switch {
    [CmdletBinding()]
    Param(
        [Parameter(Position=0, Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [System.Collections.IDictionary] $CaseMap,

        [Parameter(Position=1, Mandatory=$false)]
        [ValidateSet('Regex', 'Wildcard', 'Exact')]
        [string] $SwitchMode,

        [Parameter(Position=2, Mandatory=$false)]
        $DefaultCase,

        [Parameter(Position=3, Mandatory=$true, ValueFromPipeline=$true)]
        [AllowNull()]
        $InputObject,

        [Parameter()]
        [switch] $CaseSensitive)

    begin {
        New-Variable mode; [string] $mode = $null
        if($PSBoundParameters.ContainsKey('SwitchMode')) {
            $mode = " -$SwitchMode"
        }

        New-Variable caseOption; [string] $caseOption = $null
        if($CaseSensitive.IsPresent) {
            $caseOption = ' -CaseSensitive'
        }

        Set-Variable mode, caseOption -Option ReadOnly

        New-Variable value
        New-Variable cases; [string] $cases =
            $castDictionaryEntries.Invoke($CaseMap) `
                | % { $value = $_.Value; return $_.Key } `
                | repr `
                | % { return $_ } `
                    -End {
                            if($PSBoundParameters.TryGetValue(
                                'DefaultCase', [ref] $value))
                            {
                                Write-Output default
                            }
                        } `
                | % { return "$_ { Write-Output (,$(repr $value)) }" } `
                | Join-String -Separator ' '
        Set-Variable cases -Option ReadOnly

        New-Variable switchScript; [scriptblock] `
            $switchScript = $PSCmdlet.InvokeCommand.NewScriptBlock(
                "switch$mode$caseOption (`$InputObject) { $cases }")
        Set-Variable switchScript -Option ReadOnly

        $PSCmdlet.WriteDebug($switchScript)
    }

    process {
        . $switchScript | % { $PSCmdlet.WriteObject($_) }
    }
}
