Private/Set-HDTApplicationLine.ps1
|
function Set-HDTApplicationLine { <# .SYNOPSIS Replaces one top-level key of an app.yaml, leaving every other line byte-identical. .DESCRIPTION THE SPLICE BEHIND Set-HDTApplication, and the reason that command does not parse-then-write. app.yaml is hand-edited from the day it is written - the sample catalog is half commentary - and a round trip through the YAML writer drops every comment in the file. So the document stays text, and only the lines belonging to the named key are rewritten. A KEY'S BLOCK IS THE KEY LINE PLUS EVERYTHING INDENTED UNDER IT. That is what makes 'detect' replaceable as a whole: swapping an msiProduct rule for a file rule has to take productCode with it, or the file would carry a key the new type does not allow and the validator would refuse the result. A blank line, a comment at column 0 and the next top-level key all end the block. AN ABSENT KEY IS APPENDED. There is no way to guess where an author would have put it, and inventing a position would move the lines around it - which is the one thing this function exists not to do. IT IS TEXT IN AND TEXT OUT. Nothing here reads or writes a file, and nothing here validates: Set-HDTApplication holds the spliced result to Assert-HDTApplicationDocument before any of it reaches the share. .PARAMETER Line The document, already split into lines. .PARAMETER Key The top-level key to replace - 'name', 'install', 'detect' and so on. .PARAMETER Text The lines to write in its place. Empty removes the key and its block. .INPUTS None. This command does not accept pipeline input. .OUTPUTS System.String[] - the document, spliced. .EXAMPLE Set-HDTApplicationLine -Line $line -Key 'name' -Text @('name: 7-Zip 24.09 x64') .EXAMPLE Set-HDTApplicationLine -Line $line -Key 'detect' -Text @() Removes the detection rule, which is DESIGN 8's "install every time". #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Takes lines and returns lines. Nothing here reads or writes a file; Set-HDTApplication is what touches the share, and it declares SupportsShouldProcess.')] [CmdletBinding()] [OutputType([string[]])] param( [Parameter(Mandatory = $true, Position = 0)] [AllowEmptyCollection()] [AllowEmptyString()] [string[]] $Line, [Parameter(Mandatory = $true, Position = 1)] [ValidateNotNullOrEmpty()] [string] $Key, [Parameter(Mandatory = $true, Position = 2)] [AllowEmptyCollection()] [AllowEmptyString()] [string[]] $Text ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $pattern = '^{0}\s*:' -f [regex]::Escape($Key) $found = -1 for ($i = 0; $i -lt $Line.Count; $i++) { if ($Line[$i] -match $pattern) { $found = $i break } } $result = New-Object -TypeName System.Collections.ArrayList if ($found -lt 0) { foreach ($current in $Line) { [void] $result.Add($current) } foreach ($current in $Text) { [void] $result.Add($current) } return [string[]] @($result) } # The block ends at the first line that is not indented under the key. A # blank line ends it too: an author's paragraph break belongs to the file, # not to the key above it. $last = $found for ($i = $found + 1; $i -lt $Line.Count; $i++) { if ([string]::IsNullOrWhiteSpace($Line[$i])) { break } if ($Line[$i] -notmatch '^\s') { break } $last = $i } for ($i = 0; $i -lt $Line.Count; $i++) { if ($i -eq $found) { foreach ($current in $Text) { [void] $result.Add($current) } continue } if ($i -gt $found -and $i -le $last) { continue } [void] $result.Add($Line[$i]) } return [string[]] @($result) } |