PwSh.Fw.Core.psm1

<#

    .SYNOPSIS
    Resource file to export useful functions to prettify output

    .DESCRIPTION

    .NOTES
        Author: Charles-Antoine Degennes <cadegenn@gmail.com>
        New-ModuleManifest api.psd1 -RootModule api.psm1 -ModuleVersion "0.0.1" -Author "Charles-Antoine Degennes <cadegenn@gmail.com>"
#>


$Script:NS = (get-item $PSCommandPath).basename

# # Error codes enum
# Enum pwshfwERROR {
# OK = 0
# FAILED = 1
# RUNNING = 2
# MISSED = 3
# SKIPPED = 4
# UNUSED = 5
# UNKNOWN = 6
# DEAD = 7
# NOTFOUND = 8
# }

$Script:PWSHFW_VERSION = (Get-Content $PSScriptRoot/VERSION)
# $Script:ErrorPreference = "Continue";
$Script:indent = ""
$Script:prepend = " * "
$Script:titleChar = "*"
$Script:lineBreakChar = "-"

<#
    .SYNOPSIS
    Template function

    .DESCRIPTION
    Skeleton of a typical function

    .PARAMETER $string
    a string

    .EXAMPLE
    New-TemplateFunction -string "a string"

#>

function New-TemplateFunction {
    [CmdletBinding()]Param (
        [Parameter(Mandatory,ValueFromPipeLine = $true)][string]$string
    )
    Begin {
        # eenter($MyInvocation.MyCommand)
    }

    Process {
        return $obj
    }

    End {
        # eleave($MyInvocation.MyCommand)
    }
}

function Get-PwShFwModuleInfos {
    [CmdletBinding()][OutputType([String])]Param (
        # [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$string
    )
    Begin {
        # eenter($Script:NS + '\' + $MyInvocation.MyCommand)
    }

    Process {
        Write-Output "PSCommandPath = $PSCommandPath"
        Write-Output "PSScriptRoot = $PSScriptRoot"
    }

    End {
        # eleave($Script:NS + '\' + $MyInvocation.MyCommand)
    }
}

<#
    .SYNOPSIS
    Indent further calls to e*() functions

    .DESCRIPTION
    Indent with 2 spaces

    .NOTES
        TODO:
            . add parameter to override indent size
#>

function Write-Indent() {
    $Script:indent += " "
}

<#
    .SYNOPSIS
    Outdent further calls to e*() functions

    .DESCRIPTION
    un-indent for 2 spaces

    .NOTES
        TODO:
            . add parameter to override indent size
#>

function Write-Outdent() {
    if ($Script:indent.Length -gt 3) {
        $Script:indent = $Script:indent.Substring(0,$Script:indent.Length - 3)
    } else {
        $Script:indent = ""
    }
}

<#
    .SYNOPSIS
    Print a title

    .DESCRIPTION
    Print a title in green

#>

function Write-Title() {
    [CmdletBinding()]param(
        [string]$message
    )
    $hr = "*" * ($message.Length + $indent.length + 6)
    Write-Host -ForegroundColor Green ("`n`n" + $hr)
    $message = "** " + $indent + $message + " **"
    Write-Host -NoNewline -ForegroundColor Green $message
    Write-ToLogFile -Message ($hr)
    Write-ToLogFile -Message $message
}

<#
    .SYNOPSIS
    Print a message without new line

    .DESCRIPTION
    Print a message without new line

    .PARAMETER $message
    Text to display on screen

    .PARAMETER $width
    Optional. Used to pad text to the left.

#>

function Write-Begin() {
    [CmdletBinding()]param(
        [string]$message,
        [int32]$width = $Host.UI.RawUI.WindowSize.Width
    )
    $fullMessage = $prepend + $indent + $message + "... "
    #$ht = "." * ($Host.UI.RawUI.WindowSize.Width - $message.Length)
    $width = ($width - 16)
    Write-Host -NoNewline -ForegroundColor DarkGreen $("`n{0,-$width}" -f $fullMessage)
    Write-ToLogFile -NoNewline -Message $fullMessage
}

<#
    .SYNOPSIS
    Add message to current line.

    .DESCRIPTION
    Add text to current line of text. No new line at the beginning, no new line at the end.

#>

function Write-Add() {
    [CmdletBinding()]param(
        [string]$message
    )
    Write-Host -NoNewline $Message
    Write-ToLogFile -NoNewline -Message $message
}

<#
    .SYNOPSIS
    Print a message depending on return code

    .DESCRIPTION
    Print a message of status code.
    All status MUST have the same length. It is used to properly align all messages

#>

function Write-End() {
    [CmdletBinding()]param(
        $errorCode
    )

    if (-not($errorCode)) { $errorCode = $false }

    switch -wildcard ($errorCode.GetTYpe().Name) {
        "Bool*" {
            switch ($errorCode) {
                $true  { $color = "Green"   ; $message = " ok " }
                $false { $color = "Red"     ; $message = " failed " }
            }
        }
        "Int*" {
            switch ($errorCode) {
                0    { $color = "Green";        $message = " ok " }
                1    { $color = "Red";        $message = " failed " }
                2    { $color = "DarkGreen"; $message = " running " }
                3    { $color = "Yellow";    $message = " missed " }
                4    { $color = "Gray";        $message = " skipped " }
                5    { $color = "Gray";        $message = " unused " }
                6    { $color = "Gray";        $message = " unknown " }
                7    { $color = "Red";        $message = " dead " }
                8    { $color = "Gray";        $message = "not found" }
                default     { $color = "White";        $message = $errorCode }
            }
        }
        # "pwshfwERROR" {
        # switch ($errorCode) {
        # ([pwshfwERROR]::OK) { $color = "Green"; $message = " ok " }
        # ([pwshfwERROR]::FAILED) { $color = "Red"; $message = " failed " }
        # ([pwshfwERROR]::RUNNING) { $color = "DarkGreen"; $message = " running " }
        # ([pwshfwERROR]::MISSED) { $color = "Yellow"; $message = " missed " }
        # ([pwshfwERROR]::SKIPPED) { $color = "Gray"; $message = " skipped " }
        # ([pwshfwERROR]::UNUSED) { $color = "Gray"; $message = " unused " }
        # ([pwshfwERROR]::UNKNOWN) { $color = "Gray"; $message = " unknown " }
        # ([pwshfwERROR]::DEAD) { $color = "Red"; $message = " dead " }
        # ([pwshfwERROR]::NOTFOUND) { $color = "Gray"; $message = "not found" }
        # default { $color = "White"; $message = $errorCode }
        # }
        # }
        default { $color = "White"  ; $message = $errorCode }
    }
    $message = "[ " + $message + " ]"
    Write-Host -NoNewline -ForegroundColor $color $message
    Write-ToLogFile -NoHeader -Message $message
}

<#
    .SYNOPSIS
    Print a message depending on HTTP return code

    .DESCRIPTION
    Print a message of status code.
    All status MUST have the same length. It is used to properly align all messages

    .PARAMETER $errorCode
    Numerical value of an HTTP response code

#>

function Write-WebEnd() {
    [CmdletBinding()]param(
        [int]$errorCode
    )
    switch ($errorCode) {
        200 { $color = "Green"   ; $message = " 200 " }
        404 { $color = "Red"     ; $message = " 404 " }
        default { $color = "Yellow"  ; $message = $errorCode }
    }
    $message = " [ " + $message + " ] "
    Write-Host -NoNewline -ForegroundColor $color ($message)
    if ($log) { Write-ToLogFile -NoHeader -Message $message }
}

<#
    .SYNOPSIS
    Print a message when entering a function

    .DESCRIPTION
    Print a message specifically when entering a function.

    .EXAMPLE
    # eenter ""

#>

function Write-Enter() {
    [CmdletBinding()]param(
        [string]$message
    )
    $message = ">> " + $message + "()"
    Write-Devel ($message)
    if ($Global:DEVEL) { eindent }
}

<#
    .SYNOPSIS
    Print a message when leaving a function

    .DESCRIPTION
    Print a message specifically when entering a function.

    .EXAMPLE
    # eenter ""

#>

function Write-Leave() {
    [CmdletBinding()]param(
        [string]$message
    )
    if ($Global:DEVEL) { eoutdent }
    $message = "<< " + $message + "()"
    Write-Devel ($message)
}

<#
    .SYNOPSIS
    Print a devel message

    .DESCRIPTION
    Used to print content of command

#>

function Write-Devel() {
    [CmdletBinding()]param(
        [string]$message
    )
    if ($Global:DEVEL -eq $false) { return }
    $fullmessage = $prepend + "DEV: " + $indent + $message
    Write-Host -NoNewline -ForegroundColor DarkGray ("`n" + $fullmessage)
    #Write-Debug ($indent + " " + $message)
    Write-ToLogFile -Message $fullmessage
}

<#
    .SYNOPSIS
    Print a debug message

    .DESCRIPTION
    Override Write-Debug() powershell function
    Mainly used to print Key = Valu pair

#>

function Write-MyDebug() {
    [CmdletBinding()]param(
        [string]$message
    )
    if ($Global:DEBUG -eq $false) { return }
    $fullmessage = $prepend + "DBG: " + $indent + $message
    Write-Host -NoNewline -ForegroundColor Gray ("`n" + $fullmessage)
    #Write-Debug ($indent + " " + $message)
    Write-ToLogFile -Message $fullmessage
}

<#
    .SYNOPSIS
    Print a verbose message

    .DESCRIPTION
    Override Write-Verbose() powershell function

#>

function Write-MyVerbose() {
    [CmdletBinding()]param(
        [string]$message
    )
    if ($VERBOSE -eq $false) { return }
    $fullmessage = $prepend + $indent + $message
    Write-Host -NoNewline -ForegroundColor White  ("`n" + $fullmessage)
    #Write-Verbose ($indent + " " + $message)
    Write-ToLogFile -Message $fullmessage
}

<#
    .SYNOPSIS
    Print a warning message

    .DESCRIPTION
    Override Write-Warning() powershell function

#>

function Write-MyWarning() {
    [CmdletBinding()]param(
        [string]$message
    )
    $fullmessage = $prepend + "WRN: " + $indent + $message
    Write-Host -NoNewline -ForegroundColor Yellow ("`n" + $fullmessage)
    Write-ToLogFile -Message $fullmessage
}

<#
    .SYNOPSIS
    Print an error message

    .DESCRIPTION
    Override Write-Error() powershell function

#>

function Write-MyError() {
    [CmdletBinding()]param(
        [string]$message
    )
    $fullmessage = $prepend + "ERR: " + $indent + $message
    Write-Host -NoNewline -ForegroundColor Red ("`n" + $fullmessage)
    Write-ToLogFile -Message ($fullmessage)
}

<#
    .SYNOPSIS
    Print an information message

    .DESCRIPTION
    Override Write-Information() powershell function

#>

function Write-Info() {
    [CmdletBinding()]param(
        [string]$message
    )
    $fullmessage = $prepend + $indent + $message
    Write-Host -NoNewline -ForegroundColor Gray ("`n" + $fullmessage)
    Write-ToLogFile -Message ($fullmessage)
}

<#
    .SYNOPSIS
    Print a fatal error message then exist script

    .DESCRIPTION
    Print an error message before terminate current script

    .EXAMPLE
    Write-Fatal "fatal error. Abort."

#>

function Write-Fatal() {
    [CmdletBinding()]param(
        [string]$message
    )
    Write-MyError -ErrorAction:Stop -Message $message
    Throw " Aborting."
}

<#
    .SYNOPSIS
    Wrapper to PwSh.Fw.Log's Wrte-ToLogFile().

    .DESCRIPTION
    All the Write-*() functions from this module use Write-ToLogFile(). This wrapper is here just in case the PwSh.Fw.Log module is not loaded/available.

    .PARAMETER Append
    Append message to the log file. Do not overwrite it.
    Append = $true is the default. If you want to overwrite or initiate the file, call
    Write-ToLogFile -message "Logfile initialized" -Append=$false

    .PARAMETER NoNewline
    Do not append a new line at the end of file.

    .PARAMETER NoHeader
    Do not print header informations : "date hostname scriptname". Usefull to append text to an existing line.

    .EXAMPLE
    Write-ToLogFile -message "a log entry" -append

#>

function Write-ToLogFile() {
    [CmdletBinding()]param(
        [switch]$Append,
        [switch]$NoNewLine,
        [switch]$NoHeader,
        [string]$message,
        [string]$logFile = $Global:LOG
    )
    # old method using ubounded arguments, but I failed to make it work
    # # Write-Host("`n >> " + $MyInvocation.MyCommand)
    # Write-Host($MyInvocation.PSBoundParameters | Convertto-Json)
    # Write-Host($MyInvocation.UnboundArguments | Convertto-Json)
    # $module = Get-Module PwSh.Fw.Log -ErrorAction SilentlyContinue
    # if ($null -ne $module) {
    # PwSh.Fw.Log\Write-ToLogFile $MyInvocation.PSBoundParameters
    # PwSh.Fw.Log\Write-ToLogFile ($MyInvocation.UnboundArguments).ToString()
    # }
    # # Write-Host("`n << " + $MyInvocation.MyCommand)

    # new method with bounded parameters
    $module = Get-Module PwSh.Fw.Log -ErrorAction SilentlyContinue
    if ($null -ne $module) {
        PwSh.Fw.Log\Write-ToLogFile -Append:$Append -NoNewLine:$NoNewLine -NoHeader:$NoHeader -Message "$Message" -logFile $logFile
    }

}

<#
    .SYNOPSIS
    Execute a DOS command.

    .DESCRIPTION
    Wrapper for executing a DOS command. It handle (not yet) logging, (not yet) simulating and (not yet) asking.
    Please use following syntax :
    $rc = Execute-Commande "commande.exe" "arguments"
    to catch return code. Otherwise it will be printed to stdout and its quite ugly.

    .PARAMETER exe
    full path to executable

    .PARAMETER args
    all arguments enclosed in double-quotes. You may have to escape inner quotes to handle args with special characters.

    .PARAMETER AsInt
    Return code will be an int instead of a boolean.

    .EXAMPLE
    $rc = Execute-Command "net" "use w: \\srv\share"

#>

function Execute-Command() {
    [CmdletBinding()]param(
        [parameter(mandatory=$true, position=0)][string]$exe,
        [parameter(mandatory=$false, position=1, ValueFromRemainingArguments=$true)][string]$args,
        [switch]$AsInt
    )

    $cmd = Get-Command -Name "$exe"
    switch ($cmd.CommandType) {
        'Application' {
            $exe = "& '" + $exe + "'"
            break
        }
        'ExternalScript' {
            $exe = "& '" + $exe + "'"
            break
        }
        'Cmdlet' {

        }
        default {

        }
    }

    Write-MyDebug ($exe + " " + $args)
    $rcFile = $([System.IO.Path]::GetTempPath() + [IO.Path]::DirectorySeparatorChar + "rc")
    # edevel("rcFile = $rcFile")
    # try {
        if ($Global:DEVEL) {
            if ($AsInt) {
                Invoke-Expression ("$exe $args; `$LastExitCode | Out-File '$rcFile'") | Foreach-Object { edevel $_ }
            } else {
                Invoke-Expression ("$exe $args; `$? | Out-File '$rcFile'") | Foreach-Object { edevel $_ }
            }
            #return $?
        } elseif ($Global:DEBUG) {
            if ($AsInt) {
                Invoke-Expression ("$exe $args; `$LastExitCode | Out-File '$rcFile'") | Out-Null
            } else {
                Invoke-Expression ("$exe $args; `$? | Out-File '$rcFile'") | Out-Null
            }
            #return $?
        } else {
            if ($AsInt) {
                Invoke-Expression ("$exe $args; `$LastExitCode | Out-File '$rcFile'") | Out-Null
            } else {
                Invoke-Expression ("$exe $args; `$? | Out-File '$rcFile'") | Out-Null
            }
            #return $?
        }
        $rc = Get-Content "$rcFile"
        # edevel("rc = $rc")
        Remove-Item "$rcFile"
        # edevel("return $rc")
        # if ($null -ne $rc) {
            return $rc
        # } else {
        # return $false
        # }
    # return $true
    # } catch {
    # return $false
    # }
}

<#
    .SYNOPSIS
    Wrapper for Import-Module

    .DESCRIPTION
    It handle everything to not worry about error messages.
    * check if module exist in module path
    * if an absolute filename is given, check if module exist as well as manifest
    * load-it with an optional $Force parameter
    * write a warning if module cannot be found

    .PARAMETER Name
    Name of the module to load

    .PARAMETER FullyQualifiedName
    Absolute path and name of the module to load. It can be either the manifest file (.psd1) or the module file (.psm1)

    .PARAMETER Force
    Force a reload if module is already loaded

#>

function Load-Module {
    [CmdletBinding(
        DefaultParameterSetName="NAME"
    )]
    [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "", Justification="Load-Module is a more intuitive verb for this function and does not conflict with default Get-Module cmdlet.")]
    Param (
        [Parameter(ParameterSetName="NAME",Mandatory,ValueFromPipeLine = $true)]
        [string]$Name,
        [Parameter(ParameterSetName="FILENAME",Mandatory,ValueFromPipeLine = $true)]
        [string]$FullyQualifiedName,
        [switch]$Force,
        [ValidateSet('Required', 'Optional')]
        [string]$Policy = "Required"
    )
    Begin {
        # eenter($MyInvocation.MyCommand)
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            "NAME" {
                $module = Get-Module -ListAvailable $Name
                if ($null -eq $module) {
                    # fake module to display correct informations
                    # PowerShell < 5 does not return anything
                    $module = @{ name = $Name; path = $null}
                }
                break
            }
            "FILENAME" {
                $module = Get-Module -ListAvailable $FullyQualifiedName
                if ($null -eq $module) {
                    # fake module to display correct informations
                    # PowerShell < 5 does not return anything
                    $module = @{ name = (Split-Path -Leaf $FullyQualifiedName); path = $FullyQualifiedName}
                }
                break
            }
        }

        if ($Global:VERBOSE) {
            if ($GLOBAL:DEBUG) {
                ebegin("Importing module $($module.name) from '$($module.Path)'")
            } else {
                ebegin("Importing module $($module.name)")
            }
        }
        switch ($Policy) {
            'Required' {
                $ErrorAction = 'Continue'
            }
            'Optional' {
                $ErrorAction = 'Ignore'
                $rc = $?
            }
        }

        switch ($PSCmdlet.ParameterSetName) {
            "NAME" {
                Import-Module -Name $module.name -Global -Force:$Force -DisableNameChecking -ErrorAction $ErrorAction
                $rc = $?
                break
            }
            "FILENAME" {
                # -FullyQualifiedName is not supported in PS < 5
                if ($PSVersionTable.PSVersion.Major -lt 5) {
                    Import-Module $module.Path -Global -Force:$Force -DisableNameChecking -ErrorAction $ErrorAction
                } else {
                    Import-Module -FullyQualifiedName $module.Path -Global -Force:$Force -DisableNameChecking -ErrorAction $ErrorAction
                }
                $rc = $?
                break
            }
        }

        switch ($Policy) {
            'Required' {
                if ($rc -eq $false) {
                    efatal("Module $($module.name) was not found and policy is '$Policy'.")
                }
            }
            'Optional' {
            }
        }

        if ($Global:VERBOSE) {
            eend $rc
        }

        return $rc
    }

    End {
        # eleave($MyInvocation.MyCommand)
    }
}

<#
.SYNOPSIS
Test if a file exist

.DESCRIPTION
Long description

.EXAMPLE
An example

.NOTES
General notes
#>

function Test-FileExist {
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    Param (
        [Parameter(Mandatory,ValueFromPipeLine = $true)]
        [AllowEmptyString()][AllowNull()]
        [string]$Name
    )
    Begin {
        # eenter($MyInvocation.MyCommand)
    }

    Process {
        switch ($Name) {
            $null { return $false }
            "" { return $false }
            default {
                Test-Path $Name -PathType Leaf
            }
        }
    }

    End {
        # eleave($MyInvocation.MyCommand)
    }
}

<#
.SYNOPSIS
Test if a directory exist

.DESCRIPTION
Long description

.EXAMPLE
An example

.NOTES
General notes
#>

function Test-DirExist {
    [CmdletBinding()][OutputType([System.Boolean])]Param (
        [Parameter(Mandatory,ValueFromPipeLine = $true)]
        [AllowEmptyString()][AllowNull()]
        [string]$Path
    )
    Begin {
        # eenter($MyInvocation.MyCommand)
    }

    Process {
        switch ($Path) {
            $null { return $false }
            "" { return $false }
            default {
                Test-Path $Path -PathType Container
            }
        }
    }

    End {
        # eleave($MyInvocation.MyCommand)
    }
}

<#
    .SYNOPSIS
    Test if a registry property exist

    .DESCRIPTION
    There is not yet a builtin function to test existence of registry value. Thanks to Jonathan Medd, here it is.

    .PARAMETER RegPath
    Registry path to the key

    .EXAMPLE
    Test-RegKeyExist -RegPath HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion

    .NOTES
    General notes

    .LINK
    https://www.jonathanmedd.net/2014/02/testing-for-the-presence-of-a-registry-key-and-value.html
#>


function Test-RegKeyExist {
    param (
        [Alias('Path')]
        [parameter(Mandatory=$true)][ValidateNotNullOrEmpty()]$RegPath
    )

    Test-Path -Path $RegPath -PathType Container
}

<#
    .SYNOPSIS
    Test if a registry property exist

    .DESCRIPTION
    There is not yet a builtin function to test existence of registry value. Thanks to Jonathan Medd, here it is.

    .PARAMETER RegPath
    Registry path to the key

    .PARAMETER Name
    Name of the value to test

    .EXAMPLE
    Test-RegValueExist -RegPath HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion -Value ProgramFilesDir

    .NOTES
    General notes

    .LINK
    https://www.jonathanmedd.net/2014/02/testing-for-the-presence-of-a-registry-key-and-value.html
#>


function Test-RegValueExist {
    param (
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]$RegPath,
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]$Name
    )

    try {
        Get-ItemProperty -Path $RegPath | Select-Object -ExpandProperty $Name -ErrorAction Stop
        return $true
    } catch {
        return $false
    }
}

<#
    .SYNOPSIS
    Test if a registry value exist

    .NOTES
    # ! deprecated: use Test-RegValueExist

    .LINK
    https://www.jonathanmedd.net/2014/02/testing-for-the-presence-of-a-registry-key-and-value.html
#>

function Test-RegistryProperty {
    param (
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]$Path,
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]$Name
    )

    ewarn("Test-RegistryProperty() is deprecated. Use Test-RegValueExist() instead.")
    Test-RegValueExist -RegPath $Path -Value -Name
}

<#
    .SYNOPSIS
    Test if a variable exist

    .DESCRIPTION
    This function is silent and only return $true if the variable exist or $false otherwise.

    .PARAMETER Name
    a variable name to test

    .EXAMPLE
    Test-Variable -Name "myvar"
#>

function Test-Variable {
    [CmdletBinding()]Param (
        [Parameter(Mandatory,ValueFromPipeLine = $true)][string]$Name
    )
    Begin {
        # eenter($MyInvocation.MyCommand)
    }

    Process {
        Get-Variable -Name $Name -ErrorAction SilentlyContinue | Out-Null
        return $?
    }

    End {
        # eleave($MyInvocation.MyCommand)
    }
}

<#
.SYNOPSIS
Resolve boolean well-known values

.DESCRIPTION
Boolean are not just (true | false) value. It can by yes/no or 0/1. Resolve-Boolean handle all of this.

.PARAMETER var
The variable name to check

.EXAMPLE
true | Resolve-Boolean

.EXAMPLE
0 | Resolve-Boolean

.EXAMPLE
$test = "yes"
if ((Resolve-Boolean -var $test) -eq $true) { echo "yes is true" }

.NOTES
General notes
#>


function Resolve-Boolean {
    [CmdletBinding()][OutputType([boolean])]Param (
        [Parameter(Mandatory,ValueFromPipeLine = $true)]$var
    )
    Begin {
        # eenter($MyInvocation.MyCommand)
    }

    Process {
        switch -regex ($var.GetType()) {
            'bool*' {
                return $var
            }
            'int*' {
                switch ($var) {
                    0         { return $false }
                    1         { return $true  }
                }
            }
            'string' {
                switch -wildcard ($var) {
                    'false' { return $false }
                    'true'    { return $true  }
                    'n*'    { return $false }
                    'y*'    { return $true  }
                }
            }
        }
        return $var
    }

    End {
        # eleave($MyInvocation.MyCommand)
    }
}

<#
.SYNOPSIS
Add a custom path to the PSModulePath environment variable.

.DESCRIPTION
Add a custom path to the PSModulePath environment variable.

.PARAMETER Path
Path to add

.PARAMETER First
If specified, add the Path at the beginning of PSModulePath

.EXAMPLE
Add-PSModulePath -Path c:\MyProject\MyModules

.EXAMPLE
"C:\MyProject\MyModules" | Add-PSModulePath -First

.NOTES
General notes
#>

function Add-PSModulePath {
    [CmdletBinding()]Param (
        [Parameter(Mandatory,ValueFromPipeLine = $true)][string]$Path,
        [switch]$First
    )
    Begin {
        # eenter($Script:NS + '\' + $MyInvocation.MyCommand)
    }

    Process {
        try {
            if ($First) {
                $env:PSModulePath = $PATH + [IO.Path]::PathSeparator + $env:PSModulePath
            } else {
                $env:PSModulePath = $env:PSModulePath + [IO.Path]::PathSeparator + $PATH
            }
        } catch {
            ewarn($_.Exception.ItemName + ": " + $_.Exception.Message)
        }
        # edevel("env:PSModulePath = " + $env:PSModulePath)
    }

    End {
        # eleave($Script:NS + '\' + $MyInvocation.MyCommand)
    }
}

<#
.SYNOPSIS
Set the PSModulePath environment variable with default value

.DESCRIPTION
Set-PSModulePath remove duplicates and adds your script's Modules. It is called by skel.ps1 (and by your scripts) to set a default working environment.

.EXAMPLE
Set-PSModulePath

.NOTES

#>

function Set-PSModulePath {
    [CmdletBinding()]Param (
        # [Parameter(Mandatory,ValueFromPipeLine = $true)][string]$string
    )
    Begin {
        # eenter($Script:NS + '\' + $MyInvocation.MyCommand)
    }

    Process {
        try {
            # delete double
            # WARNING this will rearrange the order of PSModulePath !!
            $env:PSModulePath = (($env:PSModulePath -split [IO.Path]::PathSeparator | Sort-Object -Unique) -join [IO.Path]::PathSeparator).Trim([IO.Path]::PathSeparator)
            if (![string]::IsNullOrEmpty($GLOBAL:DIRNAME)) {
                $env:PSModulePath = $($Global:DIRNAME + [IO.Path]::DirectorySeparatorChar + "Modules") + [IO.Path]::PathSeparator + $env:PSModulePath
            }
        } catch {
            ewarn($_.Exception.ItemName + ": " + $_.Exception.Message)
        }
        # edevel("env:PSModulePath = " + $env:PSModulePath)
    }

    End {
        # eleave($Script:NS + '\' + $MyInvocation.MyCommand)
    }
}

Set-Alias -Force -Name eindent        -Value Write-Indent
Set-Alias -Force -Name eoutdent        -Value Write-Outdent
Set-Alias -Force -Name etitle        -Value Write-Title
Set-Alias -Force -Name ebegin        -Value Write-Begin
Set-Alias -Force -Name eadd            -Value Write-Add
Set-Alias -Force -Name eend            -Value Write-End
Set-Alias -Force -Name ewend        -Value Write-WebEnd
Set-Alias -Force -Name eenter        -Value Write-Enter
Set-Alias -Force -Name eleave        -Value Write-Leave
#Set-Alias -Force -Name einfo -Value Write-Information
Set-Alias -Force -Name einfo        -Value Write-Info
#Set-Alias -Force -Name everbose -Value Write-Verbose
Set-Alias -Force -Name everbose        -Value Write-MyVerbose
#Set-Alias -Force -Name edebug -Value Write-Debug
Set-Alias -Force -Name edebug        -Value Write-MyDebug
Set-Alias -Force -Name edevel        -Value Write-Devel
Set-Alias -Force -Name ewarn        -Value Write-MyWarning
Set-Alias -Force -Name eerror        -Value Write-MyError
Set-Alias -Force -Name efatal        -Value Write-Fatal
Set-Alias -Force -Name eexec        -Value Execute-Command
Set-Alias -Force -Name Execute-Cmd    -Value Execute-Command
Set-Alias -Force -Name Exec-Command -Value Execute-Command
Set-Alias -Force -Name Exec-Cmd        -Value Execute-Command
Set-Alias -Force -Name fileExist    -Value Test-FileExist
Set-Alias -Force -Name dirExist        -Value Test-DirExist
Set-Alias -Force -Name regKeyExist    -Value Test-RegKeyExist
Set-Alias -Force -Name regValueExist    -Value Test-RegValueExist
Set-Alias -Force -Name varExist        -Value Test-Variable
Set-Alias -Force -Name Resolve-Bool -Value Resolve-Boolean

Export-ModuleMember -Function * -Alias *