Includes/PwSh.Fw.Write.psm1
using namespace System.Management.Automation <# .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>" #> if (!($Global:QUIET)) { $Global:QUIET = $false } $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:indent = "" $Script:prepend = " * " $Script:postpend = "" $Script:DisplaySeverity = $true $Script:titleChar = "*" $Script:lineBreakChar = "-" $Script:RCPos = 'END' $Script:RCLength = 8 $Script:RCOpenChar = '[' $Script:RCCloseChar = ']' function Set-ReturnCodePosition { [CmdletBinding()] [OutputType([String])] Param ( [ValidateSet('BEGINNIG', 'END')] [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$position ) Begin { Write-EnterFunction } Process { $Script:RCPos = $position } End { Write-LeaveFunction } } <# .SYNOPSIS Set new indentation .DESCRIPTION Set-Indent permit user to set indentation regardless of where indentation actually is. .PARAMETER String String parameter must be only composed of ' ' (space) character. It sets the indentation with literal given string .PARAMETER Int Int is the number of space character to indent .EXAMPLE Set-Indent -String " " .EXAMPLE Set-Indent -Int 8 .EXAMPLE Set-Indent -String "" Resets the indentation string. It is equivalent as calling Reset-Indent .NOTES General notes .LINK #> function Set-Indent { [CmdletBinding()][OutputType([String])]Param ( [ValidatePattern('^ *$')] [Parameter(Mandatory = $true, ValueFromPipeLine = $true, ParameterSetName = 'STRING')][string]$String, [Parameter(Mandatory = $true, ValueFromPipeLine = $true, ParameterSetName = 'INT')][UInt16]$Int ) Begin { Write-EnterFunction } Process { switch ($PSCmdlet.ParameterSetName) { 'STRING' { $Script:indent = $String } 'INTEGER' { $Script:indent = " " * $Int } } } End { Write-LeaveFunction } } <# .SYNOPSIS Reset indentation to its default .DESCRIPTION Seomtimes indentation can be messed up with function that do not return properly, try-catch block, etc... In this cases it is wishable to reset indentation string. The default indentation is an empty string. .EXAMPLE Reset-Indent .NOTES General notes .LINK #> function Reset-Indent { # [CmdletBinding()][OutputType([String])]Param ( # [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$string # ) Begin { Write-EnterFunction } Process { $Script:indent = "" } End { Write-LeaveFunction } } <# .SYNOPSIS Indent further calls to e*() functions .DESCRIPTION Indent with 2 spaces .NOTES TODO: . add parameter to override indent size .LINK #> function Write-Indent() { [CmdletBinding()] [OutputType([String])] param( [switch]$PassThru ) $Script:indent += " " if ($PassThru) { return $Script:indent } } <# .SYNOPSIS Outdent further calls to e*() functions .DESCRIPTION un-indent for 2 spaces .NOTES TODO: . add parameter to override indent size .LINK #> function Write-Outdent() { [CmdletBinding()] [OutputType([String])] param( [switch]$PassThru ) if ($Script:indent.Length -gt 3) { $Script:indent = $Script:indent.Substring(0,$Script:indent.Length - 3) } else { $Script:indent = "" } if ($PassThru) { return $Script:indent } } <# .SYNOPSIS Print a title .DESCRIPTION Print a title in green .LINK #> function Write-Title() { [CmdletBinding()]param( [string]$message, [switch]$PassThru ) $hr = "*" * ($message.Length + $indent.length + 6) $message = "** " + $indent + $message + " **" Write-ToLogFile -NoNewline -Message $hr Write-ToLogFile -NoNewline -Message $message if ($PassThru) { return $message } else { if ($Global:QUIET -eq $false) { Write-Host -ForegroundColor Green "`n`n$hr" } if ($Global:QUIET -eq $false) { Write-Host -NoNewline -ForegroundColor Green $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. .LINK #> 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 if ($Global:QUIET -eq $false) { 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. .LINK #> function Write-Add() { [CmdletBinding()]param( [string]$message ) if ($Global:QUIET -eq $false) { Write-Host -NoNewline $Message } Write-ToLogFile -NoNewline -NoHeader -Message $message } function Write-ReturnCode { [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $true, ValueFromPipeLine = $true, ParameterSetName = 'STRING')][string]$string, [Parameter(Mandatory = $true, ValueFromPipeLine = $true, ParameterSetName = 'CODE')][string]$code, [ValidateSet('BEGINNIG', 'END')][string]$Position = $Script:RCPos ) switch ($PSCmdlet.ParameterSetName) { 'STRING' { $code = Get-ReturnCodeId -id "$string" } 'CODE' { $string = Get-ReturnCodeString -code $code } } $color = Get-ReturnCodeColor -code $code $message = "{0} {1,-$Script:RCLength} {2} " -f $Script:RCOpenChar, $string, $Script:RCCloseChar if ($Global:QUIET -eq $false) { if ($Host) { switch ($Position) { 'BEGINNIG' { $X = 0 $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates $X,$Host.UI.RawUI.CursorPosition.Y } 'END' { $Width = $Host.UI.RawUI.WindowSize.Width $X = $Width - $message.length $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates $X,$Host.UI.RawUI.CursorPosition.Y } } } Write-Host -NoNewline -ForegroundColor $color $message } Write-ToLogFile -NoNewline -NoHeader -Message "`t$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 .LINK #> function Write-End() { [CmdletBinding()]param( $errorCode ) if (-not($errorCode)) { $errorCode = $false } $color = "White" ; $message = $errorCode switch -wildcard ($errorCode.GetTYpe().Name) { "Bool*" { switch ($errorCode) { $true { $color = "Green" ; $message = " ok " } $false { $color = "Red" ; $message = " failed " } } } "Int*" { switch ($errorCode) { # 0 is never called since `Write-End 0` goes to the bool switch # 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" } } } # "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 } # } # } } $message = "[ " + $message + " ]" if ($Global:QUIET -eq $false) { Write-Host -NoNewline -ForegroundColor $color $message } Write-ToLogFile -NoNewline -NoHeader -Message $message } <# .SYNOPSIS Print a message when entering a function .DESCRIPTION Print a message specifically when entering a function. .EXAMPLE # eenter "" .LINK #> function Write-EnterFunction() { # [CmdletBinding()]param( # [string]$message # ) $callStack = Get-PSCallStack if ($callStack.Count -gt 1) { $message = "$($callStack[1].InvocationInfo.MyCommand.Module)\$($callStack[1].Command)" # $callStack[1] | ConvertTo-Json | Set-Content /tmp/callstack.txt # $callStack[1].InvocationInfo | ConvertTo-Json | Set-Content /tmp/callstack.InvocationInfo.txt } $message = ">> " + $message + "()" if ($Global:TRACE) { Write-Devel ($message) eindent } } <# .SYNOPSIS Print a message when leaving a function .DESCRIPTION Print a message specifically when entering a function. .EXAMPLE # eenter "" .LINK #> function Write-LeaveFunction() { # [CmdletBinding()]param( # [string]$message # ) $callStack = Get-PSCallStack if ($callStack.Count -gt 1) { $message = "$($callStack[1].InvocationInfo.MyCommand.Module)\$($callStack[1].Command)" # $callStack[1] | fl * } $message = "<< " + $message + "()" if ($Global:TRACE) { eoutdent Write-Devel ($message) } } <# .SYNOPSIS Print a message when entering something .DESCRIPTION Print a message and indent output for following messages. It is useful when entering a loop, or a module, or an external script. .EXAMPLE # eenter "" .LINK #> function Write-Enter() { [CmdletBinding()]param( [string]$message ) $message = ">> " + $message Write-Info ($message) eindent } <# .SYNOPSIS Print a message when leaving a something .DESCRIPTION Print a message specifically when entering a function. It is useful when leaving a loop, or a module, or an external script. .EXAMPLE # eenter "" .LINK #> function Write-Leave() { [CmdletBinding()]param( [string]$message ) eoutdent $message = "<< " + $message Write-Info ($message) } <# .SYNOPSIS Print a devel message .DESCRIPTION Used to print content of command .LINK #> function Write-Devel() { [CmdletBinding()]param( [string]$message ) $fullmessage = $prepend + "DEV: " + $indent + $message Write-ToLogFile -NoNewline -Message $fullmessage if ($Global:DEVEL -eq $false) { return } if ($Global:QUIET -eq $false) { Write-Host -NoNewline -ForegroundColor DarkGray ("`n" + $fullmessage) } #Write-Debug ($indent + " " + $message) } <# .SYNOPSIS Print a debug message .DESCRIPTION Override Write-Debug() powershell function Mainly used to print Key = Valu pair .LINK #> function Write-Debug() { [CmdletBinding()]param( [string]$message ) $fullmessage = $prepend + "DBG: " + $indent + $message Write-ToLogFile -NoNewline -Message $fullmessage if ($Global:DEBUG -eq $false) { return } if ($Global:QUIET -eq $false) { Write-Host -NoNewline -ForegroundColor Gray ("`n" + $fullmessage) } #Write-Debug ($indent + " " + $message) } <# .SYNOPSIS Print a verbose message .DESCRIPTION Override Write-Verbose() powershell function .LINK #> function Write-Verbose() { [CmdletBinding()]param( [string]$message ) $fullmessage = $prepend + $indent + $message Write-ToLogFile -NoNewline -Message $fullmessage if ($VERBOSE -eq $false) { return } if ($Global:QUIET -eq $false) { Write-Host -NoNewline -ForegroundColor White ("`n" + $fullmessage) } #Write-Verbose ($indent + " " + $message) } <# .SYNOPSIS Print a warning message .DESCRIPTION Override Write-Warning() powershell function .LINK #> function Write-Warning() { [CmdletBinding()]param( [string]$message ) $fullmessage = $prepend + "WRN: " + $indent + $message if ($Global:QUIET -eq $false) { Write-Host -NoNewline -ForegroundColor Yellow ("`n" + $fullmessage) } Write-ToLogFile -NoNewline -Message $fullmessage } <# .SYNOPSIS Print an error message .DESCRIPTION Override Write-Error() powershell function .LINK #> function Write-Error() { [CmdletBinding( DefaultParameterSetName = "MESSAGE" )]param( [Parameter(Mandatory = $true, ParameterSetName = 'MESSAGE')] [Parameter(Mandatory = $true, ParameterSetName = 'EXCEPTION')] [string]$Message, [string]$ErrorId, # [ValidateSet( [System.Management.Automation.ErrorCategory] )] [System.Management.Automation.ErrorCategory]$Category = "NotSpecified", [Object]$TargetObject, [string]$RecommendedAction, [string]$CategoryActivity, [string]$CategoryReason, [string]$CategoryTargetName, [string]$CategoryTargetType, [Parameter(Mandatory = $true, ParameterSetName = 'EXCEPTION')] [System.Exception]$Exception, [Parameter(Mandatory = $true, ParameterSetName = 'RECORD')] [System.Management.Automation.ErrorCategory]$ErrorRecord ) $fullmessage = $prepend + "ERR: " + $indent + $message if ($Global:QUIET -eq $false) { Write-Host -NoNewline -ForegroundColor Red ("`n$fullmessage`n") } Write-ToLogFile -NoNewline -Message ($fullmessage) # propagate to Powershell's internal Write-Error # Microsoft.PowerShell.Utility\Write-Error @PSBoundParameters } <# .SYNOPSIS Print an information message .DESCRIPTION Override Write-Information() powershell function .LINK #> function Write-Info() { [CmdletBinding()]param( [AllowEmptyString()] [string]$message ) $fullmessage = $prepend + $indent + $message Write-ToLogFile -NoNewline -Message ($fullmessage) if ($INFO -eq $false) { return } if ($Global:QUIET -eq $false) { Write-Host -NoNewline -ForegroundColor Gray ("`n" + $fullmessage) } } function Write-Message { [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$message, [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) [consolecolor]::GetNames([consolecolor]) | Where-Object { $_ -like "$wordToComplete*" } } )] [ValidateScript( { $_ -in ([consolecolor]::GetNames([consolecolor])) } )] [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$color = "Gray", [Parameter(Mandatory = $false, ValueFromPipeLine = $false)][string]$title = "" ) $fullmessage = $prepend + $title + $indent + $message if ($Global:QUIET -eq $false) { Write-Host -NoNewline -ForegroundColor $color ("`n" + $fullmessage) } Write-ToLogFile -NoNewline -Message ($fullmessage) } function Write-Todo { [CmdletBinding()] [OutputType([String])] Param ( [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$message ) Write-Message -color Magenta -title "TODO: " -Message $message } <# .SYNOPSIS Print a fatal error message then exist script .DESCRIPTION Print an error message before terminate current script .EXAMPLE Write-Fatal "fatal error. Abort." .LINK #> function Write-Fatal() { [CmdletBinding()]param( [string]$message ) Write-Error -ErrorAction:Stop -Message "$message. Aborting." try { Throw "$message. Aborting." } catch { $lines = $_.ScriptStackTrace -split "`n" # $lines.GetType() [array]::reverse($lines) edevel ("Showing stack trace :") $lines | Select-Object -SkipLast 1 | ForEach-Object { edevel $_ } } Write-Host # if (!$DEVEL) { eerror "Please run your script in devel logging with -dev parameter to see a full stack trace of the exception." } Throw "$message. Aborting." } <# .SYNOPSIS Wrapper to PwSh.Fw.Log's Write-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. .PARAMETER Message The message to write to the logfile .PARAMETER LogFile Full path to the logfile .EXAMPLE Write-ToLogFile -message "a log entry" -append .LINK #> 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:$true -NoNewLine:$NoNewLine -NoHeader:$NoHeader -Message "$Message" -logFile $logFile } } function Write-Question { [CmdletBinding()] [OutputType([String], [Int])] Param ( [Alias('Question')] [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$Prompt, [Parameter(Mandatory = $false, ValueFromPipeLine = $true)][string]$DefaultValue, [Parameter(Mandatory = $false, ValueFromPipeLine = $true)][string]$DefaultAnswer ) Begin { # Write-EnterFunction } Process { if ($DefaultAnswer) { return $DefaultAnswer } $value = Read-Host -Prompt "$Prompt [$DefaultValue]" if ([string]::IsNullOrWhiteSpace($value)) { $value = $DefaultValue } return $value } End { # Write-LeaveFunction } } Set-Alias -Force -Confirm:$false -Name eindent -Value Write-Indent Set-Alias -Force -Confirm:$false -Name eoutdent -Value Write-Outdent Set-Alias -Force -Confirm:$false -Name etitle -Value Write-Title Set-Alias -Force -Confirm:$false -Name ebegin -Value Write-Begin Set-Alias -Force -Confirm:$false -Name eadd -Value Write-Add Set-Alias -Force -Confirm:$false -Name eend -Value Write-End Set-Alias -Force -Confirm:$false -Name fenter -Value Write-EnterFunction Set-Alias -Force -Confirm:$false -Name fleave -Value Write-LeaveFunction Set-Alias -Force -Confirm:$false -Name eenter -Value Write-Enter Set-Alias -Force -Confirm:$false -Name eleave -Value Write-Leave Set-Alias -Force -Confirm:$false -Name einfo -Value Write-Info Set-Alias -Force -Confirm:$false -Name everbose -Value Write-Verbose Set-Alias -Force -Confirm:$false -Name edebug -Value Write-Debug Set-Alias -Force -Confirm:$false -Name edevel -Value Write-Devel Set-Alias -Force -Confirm:$false -Name ewarn -Value Write-Warning Set-Alias -Force -Confirm:$false -Name eerror -Value Write-Error Set-Alias -Force -Confirm:$false -Name efatal -Value Write-Fatal Set-Alias -Force -Confirm:$false -Name erc -Value Write-ReturnCode Set-Alias -Force -Confirm:$false -Name equestion -Value Write-Question # obsoletes aliases Set-Alias -Force -Confirm:$false -Name Write-MyVerbose -Value Write-Verbose Set-Alias -Force -Confirm:$false -Name Write-MyDebug -Value Write-Debug Set-Alias -Force -Confirm:$false -Name Write-MyWarning -Value Write-Warning Set-Alias -Force -Confirm:$false -Name Write-MyError -Value Write-Error |