Private/Format-AzLocalIncidentBody.ps1

function Format-AzLocalIncidentBody {
    <#
    .SYNOPSIS
        Renders a Mustache-style template against an ITSM context hashtable.
 
    .DESCRIPTION
        Supports {{path.to.value}} substitution against a nested hashtable
        (e.g. -Context @{ cluster = @{ name = 'C1' }; run = @{ id = 1 } }
        with template '{{cluster.name}} run {{run.id}}' -> 'C1 run 1').
 
        All substituted values are HTML-escaped by default; pass -NoHtmlEscape
        to disable (e.g. when rendering plain-text work-notes). Tokens that
        resolve to $null or a missing path render as an empty string, with
        the unresolved path logged via Write-Verbose.
 
        Templates may be supplied as -Template <string> or -TemplatePath <file>.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Inline')]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $true, ParameterSetName = 'Inline')]
        [AllowEmptyString()]
        [string]$Template,

        [Parameter(Mandatory = $true, ParameterSetName = 'File')]
        [ValidateNotNullOrEmpty()]
        [string]$TemplatePath,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [hashtable]$Context,

        [Parameter(Mandatory = $false)]
        [switch]$NoHtmlEscape
    )

    if ($PSCmdlet.ParameterSetName -eq 'File') {
        if (-not (Test-Path -Path $TemplatePath -PathType Leaf)) {
            throw "Incident template file not found: $TemplatePath"
        }
        $Template = Get-Content -Path $TemplatePath -Raw
    }

    if ([string]::IsNullOrEmpty($Template)) { return '' }

    $regex = [regex]'\{\{\s*([A-Za-z0-9_.]+)\s*\}\}'
    $result = $regex.Replace($Template, {
        param($m)
        $path = $m.Groups[1].Value
        $value = Resolve-AzLocalTemplatePath -Context $Context -Path $path
        if ($null -eq $value) {
            Write-Verbose "Format-AzLocalIncidentBody: token '{{$path}}' resolved to null."
            return ''
        }
        $text = [string]$value
        if (-not $NoHtmlEscape) {
            $text = [System.Net.WebUtility]::HtmlEncode($text)
        }
        return $text
    })

    return $result
}

function Resolve-AzLocalTemplatePath {
    <#
    .SYNOPSIS
        Walks a dotted path against a nested hashtable / pscustomobject.
 
    .DESCRIPTION
        Returns the resolved value or $null if any segment is missing.
        Internal helper for Format-AzLocalIncidentBody.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)][object]$Context,
        [Parameter(Mandatory = $true)][string]$Path
    )

    $current = $Context
    foreach ($segment in $Path.Split('.')) {
        if ($null -eq $current) { return $null }

        if ($current -is [hashtable] -or $current -is [System.Collections.IDictionary]) {
            if (-not $current.Contains($segment)) { return $null }
            $current = $current[$segment]
            continue
        }

        $prop = $current.PSObject.Properties[$segment]
        if (-not $prop) { return $null }
        $current = $prop.Value
    }
    return $current
}