PowerConfig.psm1

#Load Assemblies
function Import-Assembly {
<#
.SYNOPSIS
Adds Binding Redirects for Certain Assemblies to make them more flexibly compatible with Windows Powershell
#>

    [CmdletBinding()]
    param(
        #Path to the dependencies that you wish to add a binding redirect for
        [Parameter(Mandatory)][IO.FileInfo[]]$Path
    )
    if ($PSEdition -ne 'Desktop') {
        Write-Warning "Import-Assembly is only required on Windows Powershell and not Powershell Core. Skipping..."
        return
    }

    $pathAssemblies = $path.foreach{
        [reflection.assemblyname]::GetAssemblyName($PSItem)
    }
    $loadedAssemblies = [AppDomain]::CurrentDomain.GetAssemblies()
    #Bootstrap the required types in case this loads really early
    $null = Add-Type -AssemblyName mscorlib

    $onAssemblyResolveEventHandler = [ResolveEventHandler] {
        param($sender, $assemblyToResolve)

        try {
            $ErrorActionPreference = 'stop'
            [String]$assemblyToResolveStrongName = $AssemblyToResolve.Name
            [String]$assemblyToResolveName = $assemblyToResolveStrongName.split(',')[0]
            Write-Verbose "Import-Assembly: Resolving $AssemblyToResolveStrongName"

            #Try loading from our custom assembly list
            $bindingRedirectMatch = $pathAssemblies.where{
                $PSItem.Name -eq $assemblyToResolveName
            }
            if ($bindingRedirectMatch) {
                Write-Verbose "Import-Assembly: Creating a 'binding redirect' to $BindingRedirectMatch"
                return [reflection.assembly]::LoadFrom($bindingRedirectMatch.CodeBase)
            }

            #Bugfix for System.Management.Automation.resources which comes up from time to time
            #TODO: Find the underlying reason why it asks for en instead of en-us
            if ($AssemblyToResolveStrongName -like 'System.Management.Automation.Resources*') {
                $AssemblyToResolveStrongName = $AssemblyToResolveStrongName -replace 'Culture\=en\-us', 'Culture=en'
                Write-Verbose "BUGFIX: $AssemblyToResolveStrongName"
            }

            Add-Type -AssemblyName $AssemblyToResolveStrongName -ErrorAction Stop
            return [System.AppDomain]::currentdomain.GetAssemblies() | Where-Object fullname -eq $AssemblyToResolveStrongName
            #Add Type doedsn't Assume successful and return the object. This will be null if it doesn't exist and will fail resolution anyways

        } catch {
            Write-Host -fore red "Error finding $AssemblyToResolveName`: $($PSItem.exception.message)"
            return $null
        }

        #Return a null as a last resort
        return $null
    }
    [AppDomain]::CurrentDomain.add_AssemblyResolve($onAssemblyResolveEventHandler)

    Add-Type -Path $Path

    [System.AppDomain]::CurrentDomain.remove_AssemblyResolve($onAssemblyResolveEventHandler)
}

$ImportAssemblies = Get-Item "$PSScriptRoot/lib/*.dll"
if ($PSEdition -eq 'Desktop') {
    Import-Assembly -Path $ImportAssemblies
} else {
    Add-Type -Path $ImportAssemblies
}

#Add Back Extension Methods for ease of use
#TODO: Make this a method


try {
    Update-TypeData -Erroraction Stop -TypeName Microsoft.Extensions.Configuration.ConfigurationBuilder -MemberName AddYamlFile -MemberType ScriptMethod -Value {
        param([String]$Path)
        [Microsoft.Extensions.Configuration.YamlConfigurationExtensions]::AddYamlFile($this, $Path)
    }
} catch {
    if ([String]$PSItem -match 'The member .+ is already present') {
        Write-Verbose "Extension Method already present"
        $return
    }
    #Write-Error $PSItem.exception
}

try {
    Update-TypeData -Erroraction Stop -TypeName Microsoft.Extensions.Configuration.ConfigurationBuilder -MemberName AddJsonFile -MemberType ScriptMethod -Value {
        param([String]$Path)
        [Microsoft.Extensions.Configuration.JsonConfigurationExtensions]::AddJsonFile($this, $Path)
    }
} catch {
    if ([String]$PSItem -match 'The member .+ is already present') {
        Write-Verbose "Extension Method already present"
        $return
    }
    #Write-Error $PSItem.exception
}

#region SourceInit
$publicFunctions = @()
foreach ($ScriptPathItem in 'Private','Public') {
    $ScriptSearchFilter = [io.path]::Combine($PSScriptRoot, $ScriptPathItem, '*.ps1')
    $ScriptExcludeFilter = {$PSItem -notlike '*.tests.ps1' -and $PSItem -notlike '*.build.ps1'}
    Get-ChildItem $ScriptSearchFilter |
        Where-Object -FilterScript $ScriptExcludeFilter |
        Foreach-Object {
            if ($ScriptPathItem -eq 'Public') {$PublicFunctions += $PSItem.BaseName}
            . $PSItem
        }
}
Export-ModuleMember -Function $publicFunctions
#endregion SourceInit

#Fix a Powershell 5.1 issue where the strong type of the assembly for Microsoft.Extensions.FileProviders doesn't match
#This creates a generic binding redirect
#.NET Core Style Assembly Handler, where it will redirect to an already loaded assembly if present
# $bindingRedirectHandler = [ResolveEventHandler]{
# param($sender,$assembly)
# try {
# Write-Debug "BindingRedirectHandler: Resolving $($assembly.name)"
# $assemblyShortName = $assembly.name.split(',')[0]
# $matchingAssembly = [System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object fullname -match ("^" + [Regex]::Escape($assemblyShortName))
# if ($matchingAssembly.count -eq 1) {
# Write-Debug "BindingRedirectHandler: Redirecting $($assembly.name) to $($matchingAssembly.Location)"
# return $MatchingAssembly
# }
# } catch {
# #Write-Error will blackhole, which is why write-host is required. This should never occur so it should be a red flag
# write-host -fore red "BindingRedirectHandler ERROR: $PSITEM"
# return $null
# }
# return $null
# }
# [Appdomain]::CurrentDomain.Add_AssemblyResolve($bindingRedirectHandler)


# if ('AddYamlFile' -notin (get-typedata "Microsoft.Extensions.Configuration.ConfigurationBuilder").members.keys) {
# Update-TypeData -TypeName Microsoft.Extensions.Configuration.ConfigurationBuilder -MemberName AddYamlFile -MemberType ScriptMethod -Value {
# param([String]$Path)
# [Microsoft.Extensions.Configuration.YamlConfigurationExtensions]::AddYamlFile($this, $Path)
# }
# }