lib/PathsConfig.ps1
|
function Get-PathsConfig { <# .SYNOPSIS Returns the merged $Paths dictionary. Reads JSON from $ConfigFile when it exists and overlays the values on top of $Defaults; missing keys fall through to the defaults so a partial config still works. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string] $ConfigFile, [Parameter(Mandatory)] [System.Collections.IDictionary] $Defaults ) if (-not (Test-Path -Path $ConfigFile -PathType Leaf)) { return $Defaults } try { $raw = Get-Content -Path $ConfigFile -Raw -Encoding UTF8 $parsed = $raw | ConvertFrom-Json -ErrorAction Stop } catch { throw "Failed to parse '$ConfigFile': $($_.Exception.Message)" } if ($null -eq $parsed) { return $Defaults } # Most $Paths keys come from a flat top-level entry in the JSON. A # couple live inside a per-task nested object so related settings # cluster together (Wsl.Home alongside Wsl.Config / Wsl.Distros; # DockerDesktop.Home alongside DockerDesktop.WslHome). Map those # explicitly here. $nestedMap = @{ 'WslHome' = @('Wsl', 'Home') 'DockerHome' = @('DockerDesktop', 'Home') } $merged = [ordered]@{} foreach ($key in $Defaults.Keys) { $value = $null if ($nestedMap.ContainsKey($key)) { $node = $parsed foreach ($segment in $nestedMap[$key]) { if ($null -eq $node) { break } if (-not $node.PSObject.Properties[$segment]) { $node = $null; break } $node = $node.PSObject.Properties[$segment].Value } if ($node -is [string] -and $node) { $value = $node } } elseif ($parsed.PSObject.Properties[$key]) { $value = [string]$parsed.PSObject.Properties[$key].Value } $merged[$key] = if ($null -ne $value) { $value } else { $Defaults[$key] } } # Note: we deliberately do NOT carry over unknown string-valued keys # from the config. Other config sections (WingetPackages, Wsl.Distros, # NodeJsVersion, ...) have their own dedicated Get-*Config loaders and # leaking them into $Paths would trip Test-PathDriveAvailability (it # would treat "22" as a path and try to validate its drive root). return $merged } |