pf-var.ps1


# Load all global settings indicated in $filename powershell scripts which can
# modify or add environment and global variables
# Note: local variables or function defined in the settings script won't be accessible after this function is called
function Import-settings([string]$filename, [switch]$required) {
    [string[]]$settingsFiles = Find-WalkUp $filename -psWorkingFolder -rootScript -scriptFolder -MaxResults ([Int]::MaxValue)
    if (-not $settingsFiles) {
        $message = " Settings file '$filename' not found "
        if ($required) {
            throw $message 
        }
        Write-Warning $message
        return
    }
    [Array]::Reverse($settingsFiles)
    foreach ($file in $settingsFiles) {
        Write-Host "Settings applied from '$file'"
        & $file
    }
}

function Get-EnvironmentVariables {
    $targetList = @(
        [System.EnvironmentVariableTarget]::Machine
        [System.EnvironmentVariableTarget]::User
        [System.EnvironmentVariableTarget]::Process)

    foreach ($target in $targetList) {
        [System.Environment]::GetEnvironmentVariables($target).GetEnumerator() |
            Sort-Object Name |
            ForEach-Object { [PSCustomObject]@{
                Target = $target
                Name = $_.Name
                Value = $_.Value
             } } 
    }
}

function ConvertFrom-EnvironmentVariables_To_Ps1([string]$AddPrefix) {
    $template_Head = @'
 
function SetEnv($target, $name, $value) {
    [Environment]::SetEnvironmentVariable($name, $value, $target)
}
 
'@
 

    $envvars = Get-EnvironmentVariables | Sort-Object Name |
              ForEach-Object { [PSCustomObject]@{
                Target = $_.target | Update-String_Enclose "'"
                Name = $_.Name | Update-String_Enclose "'"
                Value = $_.Value | Update-String_Enclose "'"
             } } 

    $pading = $envvars.Name | ForEach-Object { $_.Length } | 
        Sort-Object -Descending | Select-Object -First 1
    $pading = if ( $pading -lt 30 ) { 30 } else { $pading }

    $lines = $envvars | ForEach-Object { "SetEnv $($_.Target) $($_.Name.PadRight($pading)) $($_.Value)" } 

    $result = $template_Head + ( $lines -join "`r`n" )
    $result
}

function Get-PSScriptValue ($varValue) {
    if ( $varValue -isnot [Array] ) { 
        switch ($varValue) {
            $null { return '$null' }
            $true { return '$true' }
            $false { return '$false' }
        }
    }
    
    if ( $varValue -is [string] ) {
        $varValue = "'" + $varValue + "'"
        return $varValue
    }

    [string]$varType = '[' + $varValue.GetType() + ']'        
    if ( $varValue -is [Enum] ) {
        $varValue = "::$varValue " + "<# " + [int]$varValue +  " #>"
    }
    elseif ( Test-Number $varValue ) {
        if ( $varValue -is [int] -or  $varValue -is [double] ) {
            $varType = '' # By default a number is [Int]
        }
        $varValue = [string]$varValue
    }
    elseif ( $varValue -is [Array] ) { 
        if ( $varValue.Count -gt 0 ) {
            $varValueList = $varValue | ForEach-Object { Get-PSScriptValue $_ }
            $varValueList = [string]::Join(', ', $varValueList)
        }
        $varValue = "@($varValueList)";
        $varType = '' # Implictly [Array]
    }    
    elseif ( $varValue -is [HashTable] ) { 
        if ( $varValue.Count -gt 0 ) {
            $varValueList = $varValue.GetEnumerator() | Sort-Object Key | 
                ForEach-Object { ( Get-PSScriptValue $_.Key ) + " = " + ( Get-PSScriptValue $_.Value ) }
            $varValueList = [string]::Join('; ', $varValueList)
        }
        $varValue = "@{$varValueList}";
        $varType = '' # Implictly [HashTable]
    }    
    elseif ( $varValue -is [scriptblock] ) { 
        $varValue = "{" + $varValue + "}";
    }    
    else { 
        $varValue = "'" + $varValue + "'"
    }
    return "$varType$varValue" 
}

function Get-PSScriptValue:::Test {
    function Assert-PSScriptValue($expected) {
        $actualStr = Get-PSScriptValue $expected
        $actual = Invoke-Expression $actualStr
        $result = $actual -eq $expected
        if ($result) { return $true }
        if ( $null -eq $actual -or $null -eq $expected ) { return $false }

        $expected = Invoke-Expression $actualStr
        $result = Compare-Object $actual $expected
        if ( -not $result ) { return $true }
    
        return $actual
    }
    function Assert-PSScriptValue($expected) {
        $actualStr = Get-PSScriptValue $expected
        $actual = Invoke-Expression $actualStr
        $actualStr
        $actual.Count
        $actual | Assert $expected -extrainfo $actual, $expected 
    }

     Assert-PSScriptValue 1
     Assert-PSScriptValue 'aaaa'
     Assert-PSScriptValue $null
     Assert-PSScriptValue $null, $true, $false, $null
     Assert-PSScriptValue $VerbosePreference
     Assert-PSScriptValue ( [datetime]'12:05' )
     Assert-PSScriptValue ( [System.Management.Automation.ActionPreference]::Continue )
     Assert-PSScriptValue @(-2,-1,0,1,2, 2.5 )
     Assert-PSScriptValue @()
     Assert-PSScriptValue @{'K1' = 1; 'K2' = 2; 'K3' = 3}
     Assert-PSScriptValue @{}
     Assert-PSScriptValue { 1 + 2 }
}

function ConvertFrom-Variables_To_Ps1 {
    $varList = Get-ChildItem -path variable: | Where-Object { $_.name -notmatch 'hidden_' } 
    $maxVarNameLength = ( $varList | ForEach-Object { $_.name.Length } | 
        Measure-Object -Maximum ).Maximum
    $truncateAt = 95 - $maxVarNameLength
    $varList | Get-VariableAssigment -truncateAt $truncateAt
}

function Get-VariableAssigment {
    param (
        [Parameter(ValueFromPipeline=$true)]
        $var, 
        $truncateAt
    )
    process {
        $varName = $var.name.PadRight($maxVarNameLength)
        try {
            [string]$rhs = Get-PSScriptValue $var.Value
        }
        catch {
            $rhs = "<#Not rendered£> [Exception]'$_'" 
        }

        if ( $rhs.Length -gt $truncateAt ) {
            $rhs = $rhs.SubString(0, $truncateAt)
            $rhs = "<#Truncated#> $rhs"
        }

        "`$$varName = $rhs" 
    }
}

function Copy-Env ($from = 'Machine', $to= 'Process' ) { 
    $globalEnvVar = [Environment]::GetEnvironmentVariables($from)
    foreach($key in $globalEnvVar.Keys) {
        $value = $globalEnvVar[$key]
        [Environment]::SetEnvironmentVariable($key,$value,$to)
    }
}

function Get-EnvDiff () {
    $envMachine = [Environment]::GetEnvironmentVariables([System.EnvironmentVariableTarget]::Machine)
    $envProcess = [Environment]::GetEnvironmentVariables([System.EnvironmentVariableTarget]::Process)
    Compare-Object -ReferenceObject $envMachine.GetEnumerator() -DifferenceObject $envProcess.GetEnumerator() -Property Name
   
}    

function Set-EnvGlobal ($env, $value) {
    [Environment]::SetEnvironmentVariable($env, $value, [System.EnvironmentVariableTarget]::Process)
    [Environment]::SetEnvironmentVariable($env, $value, [System.EnvironmentVariableTarget]::Machine)
}
function Set-EnvGlobal:::Test {
    Set-EnvGlobal 'EnvGlobalTest' '123'
    $env:EnvGlobalTest | Assert '123'
}

function Get-ProcessArgument($ArgumentName, $default) {
    $function = Get-ChildItem -Path function: | Where-Object Name -EQ $ArgumentName 
    if ( $function ) {
        $result = $function.ScriptBlock.Invoke()
        if ($result) {
            return $result
        }
    }
    $variable = Get-ChildItem -Path variable: | Where-Object Name -EQ $ArgumentName 
    if ( $variable ) {
        return $variable.Value
    }
    $result = [Environment]::GetEnvironmentVariable($ArgumentName)
    if ($result) {
        return $result
    }
    return $default
}

function Get-ProcessArgument:::Test {
    Get-ProcessArgument -ArgumentName ArgNameNoExists | assert -eq $null
    Get-ProcessArgument -ArgumentName ArgNameNoExists -default 'DefaultValue' | assert -eq 'DefaultValue'

    function ArgFunc { 
        return 'AnyValue'
    }

    Get-ProcessArgument -ArgumentName ArgFunc | assert -eq 'AnyValue'

    Get-ProcessArgument -ArgumentName ArgVariable | assert -eq 1
}