Public/ConvertFrom-NSPLabeledBlockLog.ps1

function ConvertFrom-NSPLabeledBlockLog {
    <#
    .SYNOPSIS
        Reads separator-delimited blocks of Label : value lines.
    .DESCRIPTION
        Streams a text file and emits one object per block. A line made only
        of equals signs ends a block. The first colon separates label and
        value, so additional colons in the value are preserved.
    #>

    [CmdletBinding()]
    param([Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Path)

    $resolved = (Resolve-Path -LiteralPath $Path -ErrorAction Stop).ProviderPath
    $lineNumber = 0
    $recordNumber = 0
    $startLine = 0
    $fields = [ordered]@{}

    foreach ($line in [IO.File]::ReadLines($resolved)) {
        $lineNumber++
        if ($line -match '^=+$') {
            if ($fields.Count -gt 0) {
                $recordNumber++
                [pscustomobject]@{
                    RecordNumber = $recordNumber
                    StartLine = $startLine
                    EndLine = $lineNumber - 1
                    Fields = [pscustomobject]$fields
                }
                $fields = [ordered]@{}
                $startLine = 0
            }
            continue
        }
        if ([string]::IsNullOrWhiteSpace($line)) { continue }

        $colon = $line.IndexOf(':')
        if ($colon -lt 1) { throw "Expected 'Label : value' at line $lineNumber." }
        $key = $line.Substring(0, $colon).Trim()
        if ([string]::IsNullOrWhiteSpace($key)) { throw "Empty label at line $lineNumber." }
        if ($fields.Contains($key)) { throw "Duplicate label '$key' at line $lineNumber." }
        if ($startLine -eq 0) { $startLine = $lineNumber }
        $fields[$key] = $line.Substring($colon + 1).TrimStart()
    }

    if ($fields.Count -gt 0) {
        $recordNumber++
        [pscustomobject]@{
            RecordNumber = $recordNumber
            StartLine = $startLine
            EndLine = $lineNumber
            Fields = [pscustomobject]$fields
        }
    }
}