Private/ConvertFrom-HDTDismProgressLine.ps1

function ConvertFrom-HDTDismProgressLine {
    <#
        .SYNOPSIS
            Reads dism.exe's progress meter back as a whole number, or nothing.

        .DESCRIPTION
            THE LOGIC BEHIND AN ADAPTER THAT IS NOT ALLOWED ANY. New-HDTImageService
            runs dism.exe and is not unit tested - four of its five methods write
            to a disk - so it must stay branch-free (CLAUDE.md rule 1). It hands
            every line the tool writes to a callback; this decides which of them
            is a percentage, and it is pure, so the deciding is asserted against
            a captured transcript in tests/unit.

            THE METER'S SHAPE IS NOT THE OBVIOUS ONE, which is the whole reason
            this is tested against real output rather than written from memory
            (tests/fixtures/image/dism-apply-image-output.txt, captured on this
            machine):

                [ 1.0% ]
                [===========================85.0%================= ]
                [==========================100.0%==========================]

            At 1% the number floats in spaces; at 85% it has an '=' run on both
            sides; at 100% it is embedded in a solid bar with no space around it
            at all. A pattern anchored on whitespace reads the first two and
            misses the one that says the apply finished.

            NOTHING IS RETURNED FOR ANYTHING ELSE, and that includes a sentence
            with a percentage in it. dism writes prose - a banner, a version, an
            error - and a bar that jumped because an error message mentioned a
            number would be lying at the moment somebody was reading it hardest.
            The meter is required to be the whole line: brackets, then a
            percentage inside them.

            THE FRACTION IS FLOORED, NEVER ROUNDED. 99.9% is not 100%, and the
            number on a technician's screen must not say finished before the
            tool has.

        .PARAMETER Line
            One line of dism.exe output, as the call operator hands it over.

        .INPUTS
            None. This command does not accept pipeline input.

        .OUTPUTS
            System.Int32 - the percentage, 0 to 100. Nothing at all for a line
            that is not the meter.

        .EXAMPLE
            ConvertFrom-HDTDismProgressLine -Line '[==== 37.0% ] '

            37
    #>

    [CmdletBinding()]
    [OutputType([int])]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [AllowNull()]
        [AllowEmptyString()]
        [string] $Line
    )

    Set-StrictMode -Version Latest
    $ErrorActionPreference = 'Stop'

    if ([string]::IsNullOrWhiteSpace($Line)) { return }

    # THE BRACKETS ARE THE PROOF THAT IT IS A METER. '=' and space are the only
    # things dism fills the bar with, and the number sits between them with no
    # separator guaranteed on either side.
    $match = [regex]::Match($Line.Trim(), '^\[[=\s]*([0-9]+(?:\.[0-9]+)?)%[=\s]*\]$')
    if (-not $match.Success) { return }

    $percent = [double]::Parse($match.Groups[1].Value, [System.Globalization.CultureInfo]::InvariantCulture)

    return [int] [System.Math]::Floor($percent)
}