Find-Splat.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
function Find-Splat
{
    <#
    .Synopsis
        Finds commands that can be splatted to given an input.
    .Description
        Finds the commands whose input parameters match an input object, and returns an [ordered] dictionary of parameters.
    .Link
        Get-Splat
    .Link
        Use-Splat
    .Example
        @{Id=$pid} | Find-Splat -Global
    #>

    param(
    # One or more commands.
    # If not provided, commands from the current module will be searched.
    # If there is no current module, all commands will be searched.
    [Parameter(Position=0)]
    [string[]]$Command,

    # The splat
    [Parameter(ValueFromPipeline=$true,Position=1)]
    [Alias('InputObject')]
    [PSObject]$Splat,

    # If set, will look for all commands, even if Find-Splat is used within a module.
    [Alias('G')][switch]$Global,

    # If set, will look for commands within the current module.
    # To make this work within your own module Install-Splat.
    [Alias('L')][switch]$Local,

    # If provided, will look for commands within any number of loaded modules.
    [Alias('M')][string[]]$Module,

    # If set, will return regardless of if parameters map, are valid, and have enough mandatory parameters
    [switch]$Force)
    begin {
        $myModule = $MyInvocation.MyCommand.ScriptBlock.Module
        $cmdTypes = 'Function,Cmdlet,ExternalScript,Alias'
        $resolveAliases = { begin {
            $n = 0
        } process {
            $n++
            if ($t -is [int] -and $id) {
                $p = $n* 100 / $t
                Write-Progress "Resolving" "$_ " -PercentComplete $p -Id $id
            }
            if ($_.ResolvedCommand) { $_.ResolvedCommand }
            else { $_ }
        } end {
            Write-Progress 'Resolving Aliases' 'Complete' -Id $id
        } }
        $filterCmds = { process {
            foreach ($c in $Command) { if ($_ -like $c) { return $_ } }
        } }
    }
    process {
        if (-not $Splat) { return }
        $id =[Random]::new().Next()

        $commandList =
            @(if (-not $Command) {
                Write-Progress -Id $id -Activity 'Getting Commands' -Status ' '
                if ($MyModule -and $Local) {
                    $myModule.ExportedCommands.Values | . $resolveAliases | Select-Object -Unique
                } elseif ($module) {
                    foreach ($m in $Module) {
                        $rm = Get-Module $m
                        if (-not $rm) { continue }
                        $rm.ExportedCommands.Values | . $resolveAliases | Select-Object -Unique
                    }
                } else {
                    $allcmds = @($ExecutionContext.SessionState.InvokeCommand.GetCommands('*',$cmdTypes, $true))
                    $t = $allcmds.Count
                    $allcmds |. $resolveAliases | Select-Object -Unique
                }
            } elseif ($module) {
                foreach ($m in $Module) {
                    $rm = Get-Module $m
                    if (-not $rm) { continue }
                    $rm.ExportedCommands.Values |
                        . $resolveAliases | . $filterCmds |
                        Select-Object -Unique
                }
            } elseif ($Global) {
                foreach ($c in $Command) {
                    $ExecutionContext.SessionState.InvokeCommand.GetCommands($c,$cmdTypes, $true)
                }
            } elseif ($MyModule -and $Local) {
                $myModule.ExportedCommands.Values |
                        . $filterCmds | . $resolveAliases | Select-Object -Unique
            } else {
                foreach ($cmd in $Command) {
                    if ($cmd -is [string] -and $cmd.Contains('*')) {
                        $ExecutionContext.SessionState.InvokeCommand.GetCommands($cmd,$cmdTypes, $true)
                    } else {
                        $cmd
                    }
                }
            })
        $psBoundParameters.Command = $commandList
        if ($Splat -is [Collections.IDictionary]) {
            $Splat =
                if ($splat.GetType().Name -ne 'PSBoundParametersDictionary') {
                    [PSCustomObject]$Splat
                } else {
                    [PSCustomObject]([Ordered]@{} +  $Splat)
                }
        }

        $c,$t=  0, $commandList.Count
        foreach ($cmd in $commandList) {
            if ($t -gt 1) {
                $c++;$p=$c*100/$t
                Write-Progress -Id $id -Activity 'Finding Splats' -Status "$cmd " -PercentComplete $p
            }
            $Splat |
                & ${?@} $cmd -Force:$Force |
                Select-Object -Property * -ExcludeProperty SyncRoot,IsSynchronized,IsReadOnly,IsFixedSize,Count |
                & { process {
                    if ($_.PercentFit -eq 0) { return }
                    $_.pstypenames.clear()
                    $_.pstypenames.add('Find.Splat.Output')
                    $keys, $values = @($_.Keys), @($_.Values)
                    $resplat = [Ordered]@{}
                    for ($i=0;$i -lt $keys.count;$i++) {
                        $resplat[$keys[$i]] = $values[$i]
                    }
                    $_.psobject.properties.remove('Keys')
                    $_.psobject.properties.remove('Values')
                    $_.psobject.properties.Add([Management.Automation.PSNoteProperty]::new('Splat', $resplat))
                    $_
                } }
        }

        Write-Progress -Id $id -Completed -Activity 'Finding Splats' -Status 'Complete!'
    }
}