Import-Metric.ps1

function Import-Metric {
    <#
    .SYNOPSIS
        Imports Metrics that could be charted
    .DESCRIPTION
        Imports Metrics that could be charted.
        A metric is a script that generates a series of data from pipelined input.
    .EXAMPLE
        Import-Metric -Path (Get-Module PSMetrics)
    .LINK
        Get-Metric
    #>

    param(
    # The source location of the metrics.
    # This can be a string, file, directory, command, or module.
    [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
    [Alias(        
        'FromPath',
        'FromModule',
        'FromScript',
        'FromFunction',
        'FullName',
        'Path',
        'Source'
    )]    
    [ValidateScript({
    $validTypeList = [System.String],[System.IO.FileInfo],[System.IO.DirectoryInfo],[System.Management.Automation.CommandInfo],[System.Management.Automation.PSModuleInfo]
    $thisType = $_.GetType()
    $IsTypeOk =
        $(@( foreach ($validType in $validTypeList) {
            if ($_ -as $validType) {
                $true;break
            }
        }))
    if (-not $isTypeOk) {
        throw "Unexpected type '$(@($thisType)[0])'. Must be 'string','System.IO.FileInfo','System.IO.DirectoryInfo','System.Management.Automation.CommandInfo','psmoduleinfo'."
    }
    return $true
    })]
    
    $From
    )
    begin {        
        if (-not $script:ChartMetrics) {
            $script:ChartMetrics = [Ordered]@{}
        }
        $newMetrics = @()
        $MetricPattern = '@/
        (?>
            ^Metric\p{P}
            |
            [\p{P}-[-]]Metric$
            |
            \p{P}Metric\.ps1
        )
        /@'

    }
    process {
        # Since -From can be many things, but a metric has to be a command,
        # the purpose of this function is to essentially resolve many things to a command,
        # and then cache that command.
        # If -From was a string
        if ($From -is [string]) {
            # It could be a module, so check those first.
            :ResolveFromString do {
                foreach ($loadedModule in @(Get-Module)) {
                    # If we find the module, don't try to resolve -From as a path
                    if ($loadedModule.Name -eq $from) { 
                                # (just set -From again and let the function continue)
                                                $from = $fromModule = $loadedModule;break ResolveFromString                        
                            }
                        
                }
                # If we think from was a path
                $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($from)
                # attempt to resolve it
                if ($resolvedPath) {
                    $from = Get-Item -LiteralPath $resolvedPath
                }
            } while ($false)
        }
        # If -From is a module
        if ($from -is [Management.Automation.PSModuleInfo]) {
            # recursively call ourselves with all matching commands
            @($from.ExportedCommands.Values) -match $MetricPattern |
                Import-Metric
            # then, make -From a directory
            if ($from.Path) {
                $from = Get-Item ($from.Path | Split-Path) -ErrorAction SilentlyContinue
            }
        }
        
        # If -From is a directory
        if ($from -is [IO.DirectoryInfo]) {
            $FromDirectory = $from
            # recursively call ourselves with all matching scripts
            Get-ChildItem -LiteralPath $from.FullName -Recurse -File | 
                Where-Object Name -match '\.metric.ps1$' |
                Import-Metric
            return
        }
        # If -From is a file
        if ($from -is [IO.FileInfo]) {
            # and it matches the naming convention
            if ($from.Name -notmatch '\.metric\.ps1$') { return }
            # make -From a command.
            $from = $ExecutionContext.SessionState.InvokeCommand.GetCommand($from.FullName, 'ExternalScript')
        }
        # If -From is a command
        if ($from -is [Management.Automation.CommandInfo]) {
            # decorate the command
            if ($from.pstypenames -notcontains 'Metric') {
                $from.pstypenames.insert(0,'Metric')                
            }
            
            # and add it to our list of new metrics
            $newMetrics+= $from
            $script:ChartMetrics[$from.Name -replace '\.metric\.ps1$'] = $from
            $from
        }                
    }
    end {
        # If there were no metrics to add, we're done.
        if (-not $newMetrics) { return }
        $AliasedTo = 'Out-Metric'
        # Otherwise, create many aliases for each metric
        $createAliases = 
        @(foreach ($newMetric in $newMetrics) {
            $metricSafeName = $newMetric -replace '\.metric\.ps1$'
            "Set-Alias $metricSafeName $AliasedTo"
            "Set-Alias $metricSafeName📈 $AliasedTo"
            "Set-Alias $metricSafeName📉 $AliasedTo"
            "Set-Alias $metricSafeName📊 $AliasedTo"            
            "Set-Alias $metricSafeName◕ $AliasedTo"
            "Set-Alias $metricSafeName◔ $AliasedTo"
            "Set-Alias $metricSafeName∑ $AliasedTo"           
        }
        "Export-ModuleMember -Alias *"
        ) -join [Environment]::NewLine
        $TempModuleName = 
            if ($FromModule) {
                "$FromModule.Metrics"
            } elseif ($FromDirectory) {
                "$($FromDirectory.Name)Metrics"
            } else {
                "${metricSafeName}Metrics"
            }
        New-Module -Name $TempModuleName -ScriptBlock ([scriptblock]::Create($createAliases)) | 
            Import-Module -Global -Force
    }
}