Public/Remove-HDTRule.ps1

function Remove-HDTRule {
    <#
        .SYNOPSIS
            Removes one rule from a rules document, leaving every other line
            byte-identical.
 
        .DESCRIPTION
            The command an administrator types to delete a variable rule, and the
            one anything with a Remove button has to run.
 
            IT SPLICES LINES AND NEVER PARSES YAML. The parser yields a
            dictionary and a dictionary has no comments in it; a rules.yaml is
            created with a comment header carrying a worked example and collects
            an administrator's own notes from there on. A removal that
            round-tripped through the parser would hand back a file with every
            comment gone and every key reordered.
 
            THE COMMENT ABOVE THE RULE GOES WITH IT. A comment left behind
            attaches itself to whatever now sits beneath it, so the file ends up
            stating something untrue about a rule it was never about - worse than
            losing the comment.
 
            THE LAST RULE CANNOT BE REMOVED. A rules document declares at least
            one rule, so taking the last one out produces a file the engine
            refuses to load - and it would refuse at Save, after the
            administrator had done several more edits on top of it. Refusing here
            names the choice they actually have.
 
            REMOVING A RULE CHANGES WHAT THE RULES BELOW IT DO. A set: value only
            takes effect if the variable is not already resolved, so a variable
            the removed rule was winning now falls through to whichever rule
            below sets it next. That is the point of the removal, and it is worth
            knowing before it happens.
 
            IT RETURNS LINES AND WRITES NOTHING. Save-HDTRuleDocument is what
            touches the share.
 
        .PARAMETER Line
            The document, already split into lines.
 
        .PARAMETER Name
            The rule to remove.
 
        .INPUTS
            None. This command does not accept pipeline input.
 
        .OUTPUTS
            System.String[] - the document with the rule removed.
 
        .EXAMPLE
            Remove-HDTRule -Line $line -Name 'Lab subnet'
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    [OutputType([string[]])]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [AllowEmptyCollection()]
        [AllowEmptyString()]
        [string[]] $Line,

        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string] $Name
    )

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

    $all = @(Get-HDTRuleBlock -Line $Line)
    $block = Resolve-HDTRuleBlock -Line $Line -Name $Name

    if (@($all).Count -le 1) {
        $PSCmdlet.ThrowTerminatingError((New-HDTErrorRecord -Path $Name -Category InvalidOperation `
                    -Message ("'{0}' is the only rule in this document, and a rules document with no rules is not one the engine will load. Add the rule that replaces it first, or delete the file with the workspace." -f $Name)))
    }

    if (-not $PSCmdlet.ShouldProcess($Name, 'Remove from the rules')) {
        return [string[]] @($Line)
    }

    $keep = New-Object -TypeName System.Collections.ArrayList

    for ($i = 0; $i -lt $Line.Count; $i++) {
        if ($i -ge [int] $block.Start -and $i -le [int] $block.End) { continue }

        [void] $keep.Add($Line[$i])
    }

    # A removal leaves the blank line that separated the rule from the next one
    # sitting directly beneath the blank that separated it from the previous.
    # Collapsing that pair keeps the file's spacing where it was rather than
    # letting it grow a gap with every edit.
    $result = [string[]] @(Remove-HDTBlankRun -Line ([string[]] @($keep)) -At ([int] $block.Start))

    try {
        Assert-HDTRuleLine -Line $result
    } catch {
        $PSCmdlet.ThrowTerminatingError($_)
    }

    return [string[]] $result
}