functions/step.ps1

<#PSScriptInfo
.VERSION 1.2.0
.GUID 30b223d9-c262-4631-9e29-c6ee8191dc26
.AUTHOR Black Duck
.COPYRIGHT Copyright 2024 Black Duck Software, Inc. All rights reserved.
.DESCRIPTION Includes graph-related helpers.
#>


function Add-Step([Graph] $graph, [GuidedSetupStep] $step) {

    $graph.addVertex($step) | out-null
}

function Add-StepTransition([Graph] $graph, [GuidedSetupStep] $from, [GuidedSetupStep] $to) {

    Write-Debug "Adding step transition from $($from.name) to $($to.name)..."
    foreach ($neighbor in $from.getNeighbors()) {
        if ($neighbor.name -eq $to.name) {
            return
        }
    }
    $graph.addEdge([GraphEdge]::new($from, $to)) | out-null
}

function Add-StepTransitions([Graph] $graph, [GuidedSetupStep] $from, [GuidedSetupStep[]] $toSteps) {

    for ($i = 0; $i -lt $toSteps.count; $i++) {
        $from = $i -eq 0 ? $from : $toSteps[$i-1]
        $to = $toSteps[$i]
        Add-StepTransition $graph $from $to
    }
}

function Clear-HostStep() {
    if ($DebugPreference -ne 'Continue') {
        Clear-Host
        $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 0,5
    }
}

function Write-StepGraph([string] $path, [hashtable] $steps, [collections.stack] $stepsVisited) {

    "# Enter graph at https://dreampuf.github.io/GraphvizOnline (select 'dot' Engine and use Format 'png-image-element')`ndigraph G {`n" | out-file $path -force

    $linksVisited = New-Object Collections.Generic.HashSet[string]
    
    $previousStep = $null
    while ($stepsVisited.count -gt 0) {

        $step = $stepsVisited.pop()
        "$step [color=blue];" | out-file $path -append

        if ($null -eq $previousStep) {
            $previousStep = $step
            continue
        }
        
        $link = "$previousStep -> $step"
        "$link [color=blue];" | out-file $path -append

        $linksVisited.add($link) | out-null
        $previousStep = $step
    }

    $steps.keys | ForEach-Object {
        $node = $steps[$_]
        $node.getNeighbors() | ForEach-Object {
            $link = "$node -> $_"
            if (-not $linksVisited.Contains($link)) {
                "$link;" | out-file $path -append
            }
        }
    }

    "}" | out-file $path -append
}

function Invoke-GuidedSetup([string] $title, $beginState, $endStates) {

    $Host.UI.RawUI.WindowTitle = $title

    $v = $beginState
    $vStack = new-object collections.stack

    $edgeInfo = @()
    while ($true) {

        Clear-HostStep
        Write-Debug "`Previous Edges`: $edgeInfo; Running: $($v.Name)..."
        $edgeInfo = @()

        if (-not $v.Run()) {
            $v.Reset()
            if ($vStack.Count -ne 0) {
                $v = $vStack.Pop()
                $v.Reset()
            }
            continue
        }
        $vStack.Push($v)

        if ($endStates -contains $v) {
            break
        }

        $edges = $v.getEdges()
        if ($null -eq $edges) {
            throw "Unexpectedly found 0 edges associated with step $($v.Name)"
        }

        $edges | ForEach-Object {
            $edgeInfo += " $($_.endVertex) ($($_.endVertex.CanRun()))"
        }

        $next = $edges | Where-Object { $_.endVertex.CanRun() } | Select-Object -First 1 -ExpandProperty 'endVertex'
        if ($null -eq $next) {
            Write-Error "Found 0 next steps from $v, options included $($v.getNeighbors())."
        }
        $v = $next
    }
    $vStack
}