public/Pop-EnvironmentStack.ps1

#requires -Version 3
Set-StrictMode -Version Latest


function Pop-EnvironmentStack{
    <#
    .SYNOPSIS
        Changes the current Environment Variable to the Environment Variable most recently pushed onto the stack.
    .DESCRIPTION
        The Pop-EnvironmentStack cmdlet changes the current Environment Variable to the Environment Variable most recently pushed onto the stack by using the Push-Environment cmdlet.
        You can pop a Environment Variable from the default stack or from a stack that you create by using a Push-Environment command.
    .EXAMPLE
        PS C:\> Pop-EnvironmentStack
    .EXAMPLE
        PS C:\> Pop-EnvironmentStack -Peek
    .EXAMPLE
        PS C:\> Pop-EnvironmentStack -Drop
    #>

    [CmdletBinding(DefaultParametersetName="Stack", SupportsShouldProcess)]
    Param(
        # Specifies the Environment Variable stack from which the Environment Variable is popped. Enter a Environment Variable stack name.
        #
        # Without this parameter, Pop-EnvironmentStack pops a Environment Variable from the current Environment Variable stack.
        # By default, the current Environment Variable stack is the unnamed default Environment Variable stack that Windows PowerShell creates.
        # To make a Environment Variable stack the current Environment Variable stack, use the StackName parameter of Set-Environment.
        [Parameter(Position=0, ParameterSetName="Stack", ValueFromPipelineByPropertyName)]
        [Parameter(Position=0, ParameterSetName="StackDrop", ValueFromPipelineByPropertyName)]
        [String]$StackName = ""
        ,
        # Peek Replace Mode
        # "Append" : not remove Environment Variable
        [Parameter(ParameterSetName="Stack")]
        [ValidateSet("Replace", "Append")]
        [string]$ReplaceMode = "Replace"
        ,
        # Peek Environment Stack
        [Parameter(ParameterSetName="Stack")]
        [switch]$Peek
        ,
        # IgnoredKeyPattern is pattern list of keys we don't want to deal with
        # https://github.com/direnv/direnv/blob/master/internal/cmd/env_diff.go
        [Parameter(ParameterSetName="Stack")]
        [string[]]$IgnoredKeyPattern = @(
            "COMP_WORDBREAKS" 
            "PS1"
            "OLDPWD"
            "PWD"
            "SHELL"
            "SHELLOPTS"
            "SHLVL"
            "_"
            "__fish*"
            "BASH_FUNC_*"
        )
        ,
        # Remove Environment Stack Top
        # not apply Environment Variable
        [Parameter(ParameterSetName="StackDrop")]
        [switch]$Drop
    )
    Begin
    {
        function IgnoredEnv {
            Param ([string]$env_name)
            foreach ($pattern in $IgnoredKeyPattern) {
                if ($script:IsCaseSensitive) {
                    if ($env_name -clike $pattern) {
                        return $true
                    }
                }
                else {
                    if ($env_name -ilike $pattern) {
                        return $true
                    }
                }
            }
            return $false
        }
    }
    Process
    {
        if($StackName -eq ""){
            $name = $script:DefaultEnvStackName
        }else{
            $name = $StackName
        }

        if(-not $script:EnvStack.Contains($name)){
            return
        }

        $stack = $script:EnvStack[$name]

        if($Drop){
            if($PSCmdlet.ShouldProcess("StackName: ${name}", "Drop Environment Stack Top")){
                [void]$stack.Pop()
                if($stack.Count -eq 0){
                    $script:EnvStack.Remove($name)
                }
            }
            return
        }

        if($Peek -or $WhatIfPreference){
            $applyEnvs = $stack.Peek()
        }else{
            $applyEnvs = $stack.Pop()
        }

        if($stack.Count -eq 0){
            $script:EnvStack.Remove($name)
        }

        $currentEnvs = New-InternalEnvHashTable
        Get-EnvironmentVariable | ForEach-Object {
            $currentEnvs[$_.Name] = $_
        }

        if($ReplaceMode -eq "Replace"){
            $currentEnvs.Values | ForEach-Object {
                if(IgnoredEnv $_.Name){
                    Write-Debug -Message "Replace Ignored Name $($_.Name)"
                    return
                }
                if(-not $applyEnvs.Contains($_.Name)){
                    if($PSCmdlet.ShouldProcess("$($_.Name)", "Remove Environment Variable")){
                        Remove-EnvironmentVariable -LiteralName ($_.Name) -Confirm:$false
                    }
                }
            }
        }

        $applyEnvs.Values | ForEach-Object {
            if(IgnoredEnv $_.Name){
                Write-Debug -Message "Apply Ignored Name $($_.Name)"
                return
            }
            if($currentEnvs.Contains($_.Name)){
                if($currentEnvs[$_.Name].Value -cne $_.Value){
                    if($PSCmdlet.ShouldProcess("$($_.Name)", "Update Environment Variable")){
                        Set-EnvironmentVariable -Name $_.Name -Value $_.Value -Confirm:$false
                    }
                }
            }else{
                if($PSCmdlet.ShouldProcess("$($_.Name)", "New Environment Variable")){
                    Set-EnvironmentVariable -Name $_.Name -Value $_.Value -Confirm:$false
                }
            }
        }

    }

}