Functions/Public/Convert-ColourString.ps1

<#
.SYNOPSIS
Convert string to coloured format
.DESCRIPTION
Takes a string using custom colour blocks (seee https://github.com/stuartio/writecolour) to ANSI escape sequences which can be used with regular printing functions such as Write-Host
.PARAMETER InputObject
Input string to convert
.EXAMPLE
Convert-ColourString "Hello, my name is |red|Inigo Montoya|!|"
#>

function Convert-ColourString {
    [Alias('Convert-ColorString')]
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory)]
        [string]
        $InputObject
    )

    Process {
        $e = [char]0x1b
        $Reset = "$e[0m"
        $Separator = " "
        $4BitPattern = '^([0-9]{2})( [0-9]{2})?( [a-z]+)?$'
        
        # Begin by removing end values
        $ParsedInput = $InputObject

        # Replace reset character set
        if ($env:DISABLE_WC_COLOURS -eq '1' -or $env:DISABLE_WC_COLORS -eq '1') {
            $ParsedInput = $ParsedInput.Replace('|!|', '')
        }
        else {
            $ParsedInput = $ParsedInput.Replace('|!|', $Reset)
        }
        
        # Parse Input and extract colours
        $ColourPattern = '\|([^\|]+)\|'
    
        # Find colour matches
        $ColourMatches = $ParsedInput | Select-String -Pattern $ColourPattern -AllMatches
    
        if ($ColourMatches) {
            Write-Debug "Convert-ColourfulString: Found $($ColourMatches.Matches.count) matches"
            if ($env:DISABLE_WC_COLOURS -eq '1' -or $env:DISABLE_WC_COLORS -eq '1') {
                $ColourMatches.Matches | ForEach-Object {
                    Write-Debug "Parsing colour match $($_.Value)"
                    $Value = $_.Value
                    $ParsedInput = $ParsedInput.Replace($Value, '')
                    Write-Debug "ParsedInput is now: $ParsedInput"
                }
            }
            else {
                $ColourMatches.Matches | ForEach-Object {
                    Write-Debug "Parsing colour match $($_.Value)"
                    $Value = $_.Value
                    $ColourMatch = $_.Groups[1].Value
                    $Foreground = $null
                    $Background = $null
                    $Flags = $null
                    # Split on 4-bit or 8-bit colours
                    if ($ColourMatch -match $4BitPattern) {
                        Write-Debug 'Convert-ColourfulString: Detected 4-bit colour format'

                        # Extract matched components
                        $Foreground = $Matches[1]
                        if ($Matches[2]) {
                            $Background = $Matches[2].Trim()
                        }
                        if ($Matches[3]) {
                            $Flags = $Matches[3].Trim()
                    
                        }
                        Write-Debug "Found $($ColourComponents.count) components"

                        $FormattedValue = "$e["

                        # Add flags first in 4-bit
                        if ($Flags) {
                            $FlagsArray = $Flags.ToCharArray()
                            foreach ($Flag in $FlagsArray) {
                                $FormattedValue += Convert-Flag -Flag $Flag
                            }
                        }

                        # Add foreground
                        $FormattedValue += ";$Foreground"

                        # Add background
                        if ($Background) {
                            $FormattedValue += ";$Background"
                        }
                        
                        # Add trailing m
                        $FormattedValue += "m"

                        # Fix invalid '[;' sequence
                        $FormattedValue = $FormattedValue.Replace('[;', '[')

                        Write-Debug "Convert-ColourfulString: Foreground = $Foreground"
                        if ($Background) {
                            Write-Debug "Convert-ColourfulString: Background = $Background"
                        }
                        if ($Flags) {
                            Write-Debug "Convert-ColourfulString: Flags = $Flags"
                        }

                        Write-Debug "Convert-ColourfulString: FormattedValue = $($FormattedValue.Substring(1))"

                        # Update input string with formatted value
                        $ParsedInput = $ParsedInput.Replace($Value, $FormattedValue)
                    }
                    else {
                        Write-Debug 'Convert-ColourfulString: Detected 8-bit colour format'

                        # Split value into components
                        $ColourComponents = $ColourMatch.Split($Separator)
                        Write-Debug "Found $($ColourComponents.count) components"

                        # If single element then check between either foreground colour or flags
                        if ($ColourComponents.Count -eq 1) {
                            $Foreground = Get-RGB -Colour $ColourComponents[0]
                            if ($null -eq $Foreground) {
                                Write-Debug "Using single element as flags"
                                $Flags = $ColourComponents[0]
                            }
                        }
        
                        # If 2 elements, then assume the first is the foreground, the 2nd can be either background or flags
                        elseif ($ColourComponents.Count -eq 2) {
                            $Foreground = Get-RGB -Colour $ColourComponents[0]
                            $Background = Get-RGB -Colour $ColourComponents[1]
                            if ($null -eq $Background) {
                                Write-Debug "Using 2nd element as flags"
                                $Flags = $ColourComponents[1]
                            }
                        }
        
                        # If 3 elements, then 1st is foreground, 2nd is background, 3rd is flags
                        elseif ($ColourComponents.Count -eq 3) {
                            $Foreground = Get-RGB -Colour $ColourComponents[0]
                            $Background = Get-RGB -Colour $ColourComponents[1]
                            $Flags = $ColourComponents[2]
                        }
        
                        # Otherwise panic
                        else {
                            Write-Warning "Convert-ColourString: Prefix string is malformed"
                        }
            
                        # ---- Process foreground
                        $FormattedValue = "$e[38;2;$($Foreground.red);$($Foreground.green);$($Foreground.blue)"
            
                        # ---- Add background
                        if ($Background) {
                            $FormattedValue += ";48;2;$($Background.red);$($Background.green);$($Background.blue)"
                        }
        
                        # ---- Add flags, if any
                        if ($Flags) {
                            Write-Debug "Convert-ColourfulString: Flags = $Flags"
                            $FlagsArray = $Flags.ToCharArray()
                            foreach ($Flag in $FlagsArray) {
                                $FormattedValue += Convert-Flag -Flag $Flag
                            }
                        }
            
                        # Add closing char
                        $FormattedValue += "m"
            
                        # Update input string with formatted value
                        $ParsedInput = $ParsedInput.Replace($Value, $FormattedValue)
        
                        # Add trailing reset, just in case
                        $ParsedInput += $Reset
                    }
                }
            }
    
            return $ParsedInput
        }
        else {
            Write-Warning "Convert-ColourfulString: Found no colour matches"
            return $InputObject
        }
    }
}