Private/Timing/ConvertFrom-TimingSpanImportNode.ps1
|
<# .NOTES Module-internal. Dot-sourced by the psm1 before the Public\Timing\ verbs. Not exported. Recursion behind Import-TimingSpanTree. #> # --------------------------------------------------------------------------- # ConvertFrom-TimingSpanImportNode # Recursively rebuild an in-memory span node from a ConvertFrom-Json # object parsed off the export schema. The inverse of # ConvertTo-TimingSpanExportNode, kept private so the JSON-to-node mapping # has one implementation. # # Fields are read defensively via PSObject.Properties (not direct property # access) so a partially-shaped node from an older or half-written export # yields sane defaults instead of throwing under the module's StrictMode. # This is what lets Import-TimingSpanTree honour its never-throw contract: # a crashed child's export must never fail the parent's own report. # # Children are rebuilt as a List[object] so the reconstructed subtree is # the same type the live framework mints - the E2E graft step (feature 88 # C2) can append it under a part span without a type mismatch. # --------------------------------------------------------------------------- function ConvertFrom-TimingSpanImportNode { [CmdletBinding()] param( # A single node object as produced by ConvertFrom-Json over the # export schema (camelCase keys). [Parameter(Mandatory)] $Node ) $properties = $Node.PSObject.Properties # Rebuild children first (depth-first) so the node is assembled in one # pass. A missing/null children field is a leaf - an empty list, not an # error. $children = [System.Collections.Generic.List[object]]::new() $childrenProperty = $properties['children'] if ($null -ne $childrenProperty -and $null -ne $childrenProperty.Value) { foreach ($child in @($childrenProperty.Value)) { $children.Add((ConvertFrom-TimingSpanImportNode -Node $child)) | Out-Null } } $orderProperty = $properties['order'] $nameProperty = $properties['name'] $statusProperty = $properties['status'] $elapsedProperty = $properties['elapsedMs'] $sourceProperty = $properties['source'] $order = if ($null -ne $orderProperty) { [int]$orderProperty.Value } else { 0 } $name = if ($null -ne $nameProperty) { [string]$nameProperty.Value } else { '' } $status = if ($null -ne $statusProperty) { [string]$statusProperty.Value } else { 'NotStarted' } # elapsedMs is $null until a span is timed; preserve that distinction # rather than coercing an un-run node's null to 0. $elapsed = if ($null -ne $elapsedProperty -and $null -ne $elapsedProperty.Value) { [int64]$elapsedProperty.Value } else { $null } $source = if ($null -ne $sourceProperty) { [string]$sourceProperty.Value } else { $null } # Rebuild through the single node factory so the imported subtree is the # exact shape the live framework mints (Source normalisation and the # child-list default live there); pass the already-rebuilt children # through as the node's own list. New-TimingSpanNode -Order $order -Name $name -Status $status ` -ElapsedMs $elapsed -Source $source -Children $children } |