Elizium.Krayola.psm1
Set-StrictMode -Version 1.0 function Get-EnvironmentVariable { <# .NAME Get-EnvironmentVariable .Synopsis Wrapper around [System.Environment]::GetEnvironmentVariable to support unit testing. .DESCRIPTION Retrieve the value of the environment variable specified. Returns $null if variable is not found. .EXAMPLE Get-EnvironmentVariable 'KRAYOLA-THEME-NAME' #> [CmdletBinding()] [OutputType([string])] Param ( [Parameter(Mandatory = $true)] [string]$Variable ) return [System.Environment]::GetEnvironmentVariable($Variable); } function Get-IsKrayolaLightTerminal { <# .NAME Get-IsKrayolaLightTerminal .SYNOPSIS Gets the value of KRAYOLA-LIGHT-TERMINAL as a boolean .DESCRIPTION For use by applications that need to use a Krayola theme that is dependent on whether a light or dark background colour is in effect in the current terminal. #> [OutputType([boolean])] param() return -not([string]::IsNullOrWhiteSpace( (Get-EnvironmentVariable 'KRAYOLA-LIGHT-TERMINAL'))); } function Get-KrayolaTheme { <# .NAME Get-KrayolaTheme .SYNOPSIS Helper function that makes it easier for client applications to get a Krayola theme from the environment, which is compatible with the terminal colours being used. This helps keep output from different applications consistent. .DESCRIPTION If $KrayolaThemeName is specified, then it is used to lookup the theme in the global $KrayolaThemes hash-table exposed by the Krayola module. If either the theme specified does not exist or not specified, then a default theme is used. The default theme created should be compatible with the dark/lightness of the background of the terminal currently in use. By default, a dark terminal is assumed and the colours used show up clearly against a dark background. If KRAYOLA-LIGHT-TERMINAL is defined as an environment variable (can be set to any string apart from empty string/white space), then the colours chosen show up best against a light background. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseBOMForUnicodeEncodedFile', '')] [OutputType([System.Collections.Hashtable])] param ( [Parameter( Mandatory = $false, Position = 0 )] [AllowEmptyString()] [string]$KrayolaThemeName, [Parameter(Mandatory = $false)] [System.Collections.Hashtable]$Themes = $KrayolaThemes, [Parameter(Mandatory = $false)] [System.Collections.Hashtable]$DefaultTheme = @{ # DefaultTheme is compatible with dark consoles by default # 'FORMAT' = '"<%KEY%>" => "<%VALUE%>"'; 'KEY-PLACE-HOLDER' = '<%KEY%>'; 'VALUE-PLACE-HOLDER' = '<%VALUE%>'; 'KEY-COLOURS' = @('DarkCyan'); 'VALUE-COLOURS' = @('White'); "AFFIRM-COLOURS" = @("Red"); 'OPEN' = '['; 'CLOSE' = ']'; 'SEPARATOR' = ', '; 'META-COLOURS' = @('Yellow'); 'MESSAGE-COLOURS' = @('Cyan'); 'MESSAGE-SUFFIX' = ' // '; } ) [System.Collections.Hashtable]$displayTheme = $DefaultTheme; # Switch to use colours compatible with light consoles if KRAYOLA-LIGHT-TERMINAL # is set. # if (Get-IsKrayolaLightTerminal) { $displayTheme['KEY-COLOURS'] = @('DarkBlue'); $displayTheme['VALUE-COLOURS'] = @('Red'); $displayTheme['AFFIRM-COLOURS'] = @('Magenta'); $displayTheme['META-COLOURS'] = @('DarkMagenta'); $displayTheme['MESSAGE-COLOURS'] = @('Green'); } [string]$themeName = $KrayolaThemeName; # Get the theme name # if ([string]::IsNullOrWhiteSpace($themeName)) { $themeName = Get-EnvironmentVariable 'KRAYOLA-THEME-NAME'; } if ($Themes -and $Themes.ContainsKey($themeName)) { $displayTheme = $Themes[$themeName]; } return $displayTheme; } function Show-ConsoleColours { [Alias('Show-ConsoleColors')] param () <# .NAME Show-ConsoleColours .SYNOPSIS Helper function that shows all the available console colours in the colour they represent. This willl assist in the development of colour Themes. #> [Array]$colours = @('Black', 'DarkBlue', 'DarkGreen', 'DarkCyan', 'DarkRed', 'DarkMagenta', ` 'DarkYellow', 'Gray', 'DarkGray', 'Blue', 'Green', 'Cyan', 'Red', 'Magenta', 'Yellow', 'White'); foreach ($col in $colours) { Write-Host -ForegroundColor $col $col; } } [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseBOMForUnicodeEncodedFile', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] # Param() $Global:KrayolaThemes = @{ 'EMERGENCY-THEME' = @{ 'FORMAT' = '{{<%KEY%>}}={{<%VALUE%>}}'; 'KEY-PLACE-HOLDER' = '<%KEY%>'; 'VALUE-PLACE-HOLDER' = '<%VALUE%>'; 'KEY-COLOURS' = @('White'); 'VALUE-COLOURS' = @('DarkGray'); "AFFIRM-COLOURS" = @("Yellow"); 'OPEN' = '{'; 'CLOSE' = '}'; 'SEPARATOR' = '; '; 'META-COLOURS' = @('Black'); 'MESSAGE-COLOURS' = @('Gray'); 'MESSAGE-SUFFIX' = ' ֎ ' }; 'ROUND-THEME' = @{ 'FORMAT' = '"<%KEY%>"="<%VALUE%>"'; 'KEY-PLACE-HOLDER' = '<%KEY%>'; 'VALUE-PLACE-HOLDER' = '<%VALUE%>'; 'KEY-COLOURS' = @('DarkCyan'); 'VALUE-COLOURS' = @('DarkBlue'); "AFFIRM-COLOURS" = @("Red"); 'OPEN' = '••• ('; 'CLOSE' = ') •••'; 'SEPARATOR' = ' @@ '; 'META-COLOURS' = @('Yellow'); 'MESSAGE-COLOURS' = @('Cyan'); 'MESSAGE-SUFFIX' = ' ~~ ' }; 'SQUARE-THEME' = @{ 'FORMAT' = '"<%KEY%>"="<%VALUE%>"'; 'KEY-PLACE-HOLDER' = '<%KEY%>'; 'VALUE-PLACE-HOLDER' = '<%VALUE%>'; 'KEY-COLOURS' = @('DarkCyan'); 'VALUE-COLOURS' = @('DarkBlue'); "AFFIRM-COLOURS" = @("Blue"); 'OPEN' = '■■■ ['; 'CLOSE' = '] ■■■'; 'SEPARATOR' = ' ## '; 'META-COLOURS' = @('Black'); 'MESSAGE-COLOURS' = @('DarkGreen'); 'MESSAGE-SUFFIX' = ' == ' }; 'ANGULAR-THEME' = @{ 'FORMAT' = '"<%KEY%>"-->"<%VALUE%>"'; 'KEY-PLACE-HOLDER' = '<%KEY%>'; 'VALUE-PLACE-HOLDER' = '<%VALUE%>'; 'KEY-COLOURS' = @('DarkCyan'); 'VALUE-COLOURS' = @('DarkBlue'); "AFFIRM-COLOURS" = @("Blue"); 'OPEN' = '◄◄◄ <'; 'CLOSE' = '> ►►►'; 'SEPARATOR' = ' ^^ '; 'META-COLOURS' = @('Black'); 'MESSAGE-COLOURS' = @('DarkGreen'); 'MESSAGE-SUFFIX' = ' // ' } } $null = $KrayolaThemes; function Write-InColour { <# .NAME Write-InColour .SYNOPSIS Writes a multiple snippets of a line in colour with the provided text, foreground & background colours. .DESCRIPTION The user passes in an array of 1,2 or 3 element arrays, which contains any number of text fragments with an optional colour specification (ConsoleColor enumeration). The function will then write a multi coloured text line to the console. Element 0: text Element 1: foreground colour Element 2: background colour If the background colour is required, then the foreground colour must also be specified. Write-InColour -colouredTextLine @( ("some text", "Blue"), ("some more text", "Red", "White") ) Write-InColour -colouredTextLine @( ("some text", "Blue"), ("some more text", "Red") ) Write-InColour -colouredTextLine @( ("some text", "Blue"), ("some more text") ) If you only need to write a single element, use an extra , preceding the array eg: Write-InColour -colouredTextLine @( ,@("some text", "Blue") ) Empty snippets, should not be passed in, it's up to the caller to ensure that this is the case. If an empty snippet is found an ugly warning message is emitted, so this should not go un-noticed. .PARAMETER TextSnippets An array of an array of strings (see description). .PARAMETER NoNewLine Switch to indicate if a new line should be written after the text. #> # This function is supposed to write to the host, because the output is in colour. # Using Write-Host is Krayola's raison d'etre! # [Alias('Write-InColor')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '')] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string[][]] $TextSnippets, [Switch]$NoNewLine ) foreach ($snippet in $TextSnippets) { if ($snippet.Length -eq 0) { Write-Warning ' * Found malformed line (empty snippet entry), skipping * '; continue; } if ($snippet.Length -eq 1) { Write-Warning " * No colour specified for snippet '$($snippet[0])', skipping * "; continue; } if ($null -eq $snippet[0]) { Write-Warning ' * Found empty snippet text, skipping *'; continue; } if ($snippet.Length -eq 2) { if ($null -eq $snippet[1]) { Write-Warning " * Foreground col is null, for snippet: '$($snippet[0])', skipping * "; continue; } # Foreground colour specified # Write-Host $snippet[0] -NoNewline -ForegroundColor $snippet[1]; } else { # Foreground and background colours specified # Write-Host $snippet[0] -NoNewline -ForegroundColor $snippet[1] -BackgroundColor $snippet[2]; if ($snippet.Length -gt 3) { Write-Warning " * Excess entries found for snippet: '$($snippet[0])' * "; } } } if (-not ($NoNewLine.ToBool())) { Write-Host ''; } } function Write-RawPairsInColour { <# .NAME Write-RawPairsInColour .SYNOPSIS .DESCRIPTION The snippets passed in as element of $Pairs are in the same format as those passed into Write-InColour as TextSnippets. The only difference is that each snippet can only have 2 entries, the first being the key and the second being the value. .PARAMETER Pairs A 3 dimensional array representing a sequence of key/value pairs where each key and value are in themselves a sub-sequence of 2 or 3 items representing text, foreground colour & background colours. Eg: $PairsToWriteInColour = @( @(@("Sport", "Red"), @("Tennis", "Blue", "Yellow")), @(@("Star", "Green"), @("Martina Hingis", "Cyan")) ); .PARAMETER Format A string containing a placeholder for the Key and the Value. It represents how the whole key/value pair should be represented. It must contain the KEY-PLACE-HOLDER and VALUE-PLACE-HOLDER strings .PARAMETER KeyPlaceHolder The place holder that identifies the Key in the FORMAT string .PARAMETER ValuePlaceHolder The place holder that identifies the Value in the FORMAT string. .PARAMETER KeyColours Array of 1 or 2 items only, the first is the foreground colour and the optional second value is the background colour, that specifies how Keys are displayed. .PARAMETER ValueColours The same as KEY-COLOURS but it applies to Values. .PARAMETER Open Specifies the leading wrapper around the whole key/value pair collection, typically '('. .PARAMETER Close Specifies the tail wrapper around the whole key/value pair collection typically ')'. .PARAMETER Separator Specifies a sequence of characters that separates the Key/Vale pairs, typically ','. .PARAMETER MetaColours This is the colour specifier for any character that is not the key or the value. Eg: if the format is defined as ['<%KEY%>'='<%VALUE%>'], then [' '=' '] will be written in this colour. As with other write in clour functionality, the user can specify just a single colour (in a single item array), which would represent the foreground colour, or 2 colours can be specified, representing the foreground and background colours in that order inside the 2 element array (a pair). These meta colours will also apply to the Open, Close and Separator tokens. .PARAMETER MessageColours An optional message that appears preceding the Key/Value pair collection and this array describes the colours used to write that message. .PARAMETER MessageSuffix Specifies a sequence of characters that separates the MESSAGE (if present) from the Key/Value pair collection. #> # This function is supposed to write to the host, because the output is in colour. # Using Write-Host is Krayola's raison d'etre! # [Alias('Write-RawPairsInColor')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '')] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [AllowEmptyCollection()] [string[][][]] $Pairs, [Parameter(Mandatory = $false)] [string] $Format = '("<%KEY%>"="<%VALUE%>")', [Parameter(Mandatory = $false)] [string] $KeyPlaceHolder = '<%KEY%>', [Parameter(Mandatory = $false)] [string] $ValuePlaceHolder = '<%VALUE%>', [Parameter(Mandatory = $false)] [AllowEmptyString()] $Open = '=== [', [AllowEmptyString()] [Parameter(Mandatory = $false)] $Close = '] ===', [AllowEmptyString()] [Parameter(Mandatory = $false)] $Separator = ', ', [Parameter(Mandatory = $false)] [string[]] $MetaColours = @('White'), [Parameter(Mandatory = $false)] [string] $Message, [Parameter(Mandatory = $false)] [string[]] $MessageColours = @('White'), [AllowEmptyString()] [Parameter(Mandatory = $false)] [string] $MessageSuffix = ' // ', [Switch]$NoNewLine ) if ($Pairs.Length -eq 0) { return; } if (($MetaColours.Length -lt 1) -or ($MetaColours.Length -gt 2)) { Write-Error -Message "Bad meta colours spec, aborting (No of colours specified: $($MetaColours.Length))"; } # Write the leading message # if (-not([String]::IsNullOrEmpty($Message))) { [string[]]$messageSnippet = @($Message) + $MessageColours; [string[][]]$wrapper = @(, $messageSnippet); Write-InColour -TextSnippets $wrapper -NoNewLine; if (-not([String]::IsNullOrEmpty($MessageSuffix))) { [string[]]$suffixSnippet = @($MessageSuffix) + $MessageColours; [string[][]]$wrapper = @(, $suffixSnippet); Write-InColour -TextSnippets $wrapper -NoNewLine; } } # Add the Open snippet # if (-not([String]::IsNullOrEmpty($Open))) { [string[]]$openSnippet = @($Open) + $MetaColours; [string[][]]$wrapper = @(, $openSnippet); Write-InColour -TextSnippets $wrapper -NoNewLine; } [int]$fieldCounter = 0; foreach ($field in $Pairs) { [string[][]]$displayField = @(); # Each element of a pair is an instance of a snippet that is compatible with Write-InColour # which we can defer to. We need to create the 5 snippets that represents the field pair. # if ($field.Length -ge 2) { # Get the key and value # [string[]]$keySnippet = $field[0]; $keyText, $keyColours = $keySnippet; [string[]]$valueSnippet = $field[1]; $valueText, $valueColours = $valueSnippet; [string[]]$constituents = Split-KeyValuePairFormatter -Format $Format ` -KeyConstituent $keyText -ValueConstituent $valueText ` -KeyPlaceHolder $KeyPlaceHolder -ValuePlaceHolder $ValuePlaceHolder; [string[]]$constituentSnippet = @(); # Now create the 5 snippets (header, key/value, mid, value/key, tail) # # header # $constituentSnippet = @($constituents[0]) + $MetaColours; $displayField += , $constituentSnippet; # key # $constituentSnippet = @($constituents[1]) + $keyColours; $displayField += , $constituentSnippet; # mid # $constituentSnippet = @($constituents[2]) + $MetaColours; $displayField += , $constituentSnippet; # value # $constituentSnippet = @($constituents[3]) + $valueColours; $displayField += , $constituentSnippet; # tail # $constituentSnippet = @($constituents[4]) + $MetaColours; $displayField += , $constituentSnippet; Write-InColour -TextSnippets $displayField -NoNewLine; if ($field.Length -gt 2) { Write-Warning ' * Ignoring excess snippets *'; } } else { Write-Warning ' * Insufficient snippet pair, 2 required, skipping *'; } # Field Separator snippet # if (($fieldCounter -lt ($Pairs.Length - 1)) -and (-not([String]::IsNullOrEmpty($Separator)))) { [string[]]$separatorSnippet = @($Separator) + $MetaColours; Write-InColour -TextSnippets @(, $separatorSnippet) -NoNewLine; } $fieldCounter++; } # Add the Close snippet # if (-not([String]::IsNullOrEmpty($Close))) { [string[]]$closeSnippet = @($Close) + $MetaColours; [string[][]]$wrapper = @(, $closeSnippet); Write-InColour -TextSnippets $wrapper -NoNewLine; } if (-not($NoNewLine.ToBool())) { Write-Host ''; } } function Write-ThemedPairsInColour { <# .NAME Write-ThemedPairsInColour .SYNOPSIS Writes a collection of key/value pairs in colour according to a specified Theme. .DESCRIPTION The Pairs defined here are colour-less, instead colours coming from the KEY-COLOURS and VALUE-COLOURS in the theme. The implications of this are firstly, the Pairs are simpler to specify. However, the colour representation is more restricted, because all Keys displayed must be of the same colour and the same goes for the values. When using Write-RawPairsInColour directly, the user has to specify each element of a pair with 2 or 3 items; text, foreground colour & background colour, eg: @(@("Sport", "Red"), @("Tennis", "Blue", "Yellow")) Now, each pair is specified as a simply a pair of strings: @("Sport", "Tennis") The purpose of this function is to generate a single call to Write-RawPairsInColour in the form: $PairsToWriteInColour = @( @(@("Sport", "Red"), @("Tennis", "Blue", "Yellow")), @(@("Star", "Green"), @("Martina Hingis", "Cyan")) ); Write-RawPairsInColour -Message ">>> Greetings" -MessageColours @("Magenta") ` -Pairs $PairsToWriteInColour -Format "'<%KEY%>'<--->'<%VALUE%>'" ` -MetaColours @(,"Blue") -Open " ••• <<" -Close ">> •••" A value can be highlighted by specifying a boolean affirmation value after the key/value pair. So the 'value' of a pair, eg 'Tennis' of @("Sport", "Tennis") can be highlighted by the addition of a boolean value: @("Sport", "Tennis", $true), will result in 'Tennis' being highlighted; written with a different colour value. This colour value is taken from the 'AFFIRM-COLOURS' entry in the theme. If the affirmation value is false, eg @("Sport", "Tennis", $false), then the value 'Tennis' will be written as per-normal using the 'VALUE-COLOURS' entry. You can create your own theme, using this template for assistance: $YourTheme = @{ "FORMAT" = "'<%KEY%>' = '<%VALUE%>'"; "KEY-PLACE-HOLDER" = "<%KEY%>"; "VALUE-PLACE-HOLDER" = "<%VALUE%>"; "KEY-COLOURS" = @("Red"); "VALUE-COLOURS" = @("Magenta"); "AFFIRM-COLOURS" = @("White"); "OPEN" = "("; "CLOSE" = ")"; "SEPARATOR" = ", "; "META-COLOURS" = @("Blue"); "MESSAGE-COLOURS" = @("Green"); "MESSAGE-SUFFIX" = " // " } .PARAMETER Pairs A 2 dimensional array representing the key/value pairs to be rendered. .PARAMETER Theme Hash-table that must contain all the following fields FORMAT KEY-PLACE-HOLDER VALUE-PLACE-HOLDER KEY-COLOURS VALUE-COLOURS OPEN CLOSE SEPARATOR META-COLOURS MESSAGE-COLOURS MESSAGE-SUFFIX .PARAMETER Message An optional message that precedes the display of the Key/Value sequence. #> [Alias('Write-ThemedPairsInColor')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseBOMForUnicodeEncodedFile', '')] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [AllowEmptyCollection()] [string[][]] $Pairs, [Parameter(Mandatory = $true)] [System.Collections.Hashtable] $Theme, [Parameter(Mandatory = $false)] [string] $Message ) if (0 -eq $Pairs.Length) { return; } [boolean]$inEmergency = $false; function isThemeValid { param( [System.Collections.Hashtable]$themeToValidate ) [int]$minimumNoOfThemeEntries = 11; return ($themeToValidate -and ($themeToValidate.Count -ge $minimumNoOfThemeEntries)) } if (-not(isThemeValid($Theme))) { $Theme = $KrayolaThemes['EMERGENCY-THEME']; # In case the user has compromised the EMERGENCY theme, which should be modify-able (because we # can't be sure that the emergency theme we have defined is suitable for their console), # we'll use this internal emergency theme ... # if (-not(isThemeValid($Theme))) { $Theme = @{ 'FORMAT' = '{{<%KEY%>}}={{<%VALUE%>}}'; 'KEY-PLACE-HOLDER' = '<%KEY%>'; 'VALUE-PLACE-HOLDER' = '<%VALUE%>'; 'KEY-COLOURS' = @('White'); 'VALUE-COLOURS' = @('DarkGray'); 'OPEN' = '{'; 'CLOSE' = '}'; 'SEPARATOR' = '; '; 'META-COLOURS' = @('Black'); 'MESSAGE-COLOURS' = @('Gray'); 'MESSAGE-SUFFIX' = ' ֎ ' } } $inEmergency = $true; } [string[][][]] $pairsToWriteInColour = @(); # Construct the pairs # [string[]]$keyColours = $Theme['KEY-COLOURS']; [string[]]$valueColours = $Theme['VALUE-COLOURS']; foreach ($pair in $Pairs) { if (1 -ge $pair.Length) { [string[]]$transformedKey = @('!INVALID!') + $keyColours; [string[]]$transformedValue = @('---') + $valueColours; Write-Error "Found pair that does not contain 2 items (pair: $($pair)) [!!! Reminder: you need to use the comma op for a single item array]"; } else { [string[]]$transformedKey = @($pair[0]) + $keyColours; [string[]]$transformedValue = @($pair[1]) + $valueColours; # Apply affirmation # if ((3 -eq $pair.Length)) { if (($pair[2] -ieq 'true')) { if ($Theme.ContainsKey('AFFIRM-COLOURS')) { $transformedValue = @($pair[1]) + $Theme['AFFIRM-COLOURS']; } else { # Since the affirmation colour is missing, use another way of highlighting the value # ie, surround in asterisks # $transformedValue = @("*{0}*" -f $pair[1]) + $valueColours; } } elseif (-not($pair[2] -ieq 'false')) { Write-Error "Invalid affirm value found; not boolean value, found: $($pair[2]) [!!! Reminder: you need to use the comma op for a single item array]" } } elseif (3 -lt $pair.Length) { Write-Error "Found pair with excess items (pair: $($pair)) [!!! Reminder: you need to use the comma op for a single item array]" } } $transformedPair = , @($transformedKey, $transformedValue); $pairsToWriteInColour += $transformedPair; } [System.Collections.Hashtable]$parameters = @{ 'Pairs' = $pairsToWriteInColour; 'Format' = $Theme['FORMAT']; 'KeyPlaceHolder' = $Theme['KEY-PLACE-HOLDER']; 'ValuePlaceHolder' = $Theme['VALUE-PLACE-HOLDER']; 'Open' = $Theme['OPEN']; 'Close' = $Theme['CLOSE']; 'Separator' = $Theme['SEPARATOR']; 'MetaColours' = $Theme['META-COLOURS']; } if ([String]::IsNullOrEmpty($Message)) { if ($inEmergency) { $Message = 'ϞϞϞ '; } } else { if ($inEmergency) { $Message = 'ϞϞϞ ' + $Message; } } if (-not([String]::IsNullOrEmpty($Message))) { $parameters['Message'] = $Message; $parameters['MessageColours'] = $Theme['MESSAGE-COLOURS']; $parameters['MessageSuffix'] = $Theme['MESSAGE-SUFFIX']; } & 'Write-RawPairsInColour' @parameters; } function Split-KeyValuePairFormatter { <# .NAME Split-KeyValuePairFormatter .SYNOPSIS Splits an input string which should conform to the format string containing <%KEY%> and <%VALUE%> constituents. .DESCRIPTION The format string should contain key and value token place holders. This function, will split the input returning an array of 5 strings representing the constituents. .PARAMETER Format Format specifier for each key/value pair encountered. The string must contain the tokens whatever is defined in KeyPlaceHolder and ValuePlaceHolder. .PARAMETER KeyConstituent The value of the Key. .PARAMETER ValueConstituent The value of the Value! .PARAMETER KeyPlaceHolder The place holder that identifies the Key in the Format parameter. .PARAMETER ValuePlaceHolder The place holder that identifies the Value in the Format parameter. #> [OutputType([string[]])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $Format, [string] $KeyConstituent, [string] $ValueConstituent, [string] $KeyPlaceHolder = '<%KEY%>', [string] $ValuePlaceHolder = '<%VALUE%>' ) [string[]]$constituents = @(); [int]$keyPosition = $Format.IndexOf($KeyPlaceHolder); [int]$valuePosition = $Format.IndexOf($ValuePlaceHolder); if ($keyPosition -eq -1) { Write-Error -Message "Invalid formatter: '$($Format)', key: '$({$KeyPlaceHolder})' not found"; } if ($valuePosition -eq -1) { Write-Error -Message "Invalid formatter: '$($Format)', value: '$({$ValuePlaceHolder})' not found"; } # Need this check just in case the user wants Value=Key!!!, or perhaps something # exotic like [Value(Key)], ie; it's bad to make the assumption that the key comes # before the value in the format sring. # if ($keyPosition -lt $valuePosition) { [string]$header = ''; if ($keyPosition -ne 0) { # Insert everything up to the KeyFormat (the header) # $header = $Format.Substring(0, $keyPosition); } $constituents += $header; # Insert the KeyFormat # $constituents += $KeyConstituent; # Insert everything in between the key and value formats, typically the # equal sign (key=value), but it could be anything eg --> (key-->value) # [int]$midStart = $header.Length + $KeyPlaceHolder.Length; [int]$midLength = $valuePosition - $midStart; if ($midLength -lt 0) { Write-Error -Message "Internal error, couldn't get the middle of the formatter: '$Format'"; } [string]$middle = $Format.Substring($midStart, $midLength); $constituents += $middle; # Insert the value # $constituents += $ValueConstituent; # Insert everything after the ValueFormat (the tail) # 0 1 2 # 012345678901234567890 # [<%KEY%>=<%VALUE%>] # [int]$tailStart = $valuePosition + $ValuePlaceHolder.Length; # 9 + 9 [int]$tailEnd = $Format.Length - $tailStart; # 19 -18 [string]$tail = $Format.Substring($tailStart, $tailEnd); $constituents += $tail; } else { [string]$header = ''; if ($valuePosition -ne 0) { # Insert everything up to the ValueFormat (the header) # $header = $Format.Substring(0, $valuePosition); } $constituents += $header; # Insert the ValueFormat # $constituents += $ValueConstituent; # Insert everything in between the value and key formats, typically the # equal sign (value=key), but it could be anything eg --> (value-->key) # [int]$midStart = $header.Length + $ValuePlaceHolder.Length; [int]$midLength = $keyPosition - $midStart; if ($midLength -lt 0) { Write-Error -Message "Internal error, couldnt get the middle of the formatter: '$Format'"; } [string]$middle = $Format.Substring($midStart, $midLength); $constituents += $middle; # Insert the key # $constituents += $KeyConstituent; # Insert everything after the KeyFormat (the tail) # [int]$tailStart = $keyPosition + $KeyPlaceHolder.Length; [int]$tailEnd = $Format.Length - $tailStart; [string]$tail = $Format.Substring($tailStart, $tailEnd); $constituents += $tail; } return [string[]]$constituents; } Export-ModuleMember -Variable KrayolaThemes |