Sources/Hcon.ps1
|
using namespace System.Diagnostics.CodeAnalysis using namespace System.Text.RegularExpressions <# .SYNOPSIS Converts a HCON-formatted string to a hash table. .INPUTS The HCON-formatted string to convert. .OUTPUTS The hash table corresponding to the specified HCON-formatted string. #> function ConvertFrom-Hcon { [CmdletBinding()] [OutputType([hashtable])] [SuppressMessage("PSAvoidUsingEmptyCatchBlock", "")] param ( # The HCON-formatted string to convert. [Parameter(Mandatory, Position = 1, ValueFromPipeline)] [AllowEmptyString()] [string] $InputObject, # The maximum depth the HCON input is allowed to have. [ValidateRange("Positive")] [int] $Depth = 1024 ) begin { $getGroup = { param ([Match] $match, [int] $index) $value = $match.Groups[$index]?.Value; $value ? $value : $null } $hconPattern = "(?:""([^""]+)""|'([^']+)'|([^\s,:]+))(?:\s*:\s*(?:""([^""]*)""|'([^']*)'|<((?:[^/]|\/(?!>))+)\/>|([^\s,]+)))?(?=\s|,|$)" } process { $hcon = $InputObject.Trim() if (-not $hcon) { return @{} } if ($hcon -like "{*") { return ConvertFrom-Json $hcon -AsHashtable -Depth $Depth } $result = @{} foreach ($match in [regex]::Matches($hcon, $hconPattern)) { $doubleQuotedKey = & $getGroup $match 1 # "key" $singleQuotedKey = & $getGroup $match 2 # 'key' $bareKey = & $getGroup $match 3 # key $doubleQuotedValue = & $getGroup $match 4 # "value" $singleQuotedValue = & $getGroup $match 5 # 'value' $hyperscriptValue = & $getGroup $match 6 # <value/> $bareValue = & $getGroup $match 7 # value $key = $doubleQuotedKey ?? $singleQuotedKey ?? $bareKey $value = ($doubleQuotedValue ?? $singleQuotedValue ?? $hyperscriptValue ?? $bareValue ?? "true").Trim() try { $value = ConvertFrom-Json $value -AsHashtable -Depth $Depth -ErrorAction Stop } catch {} if ($bareKey -notlike "*.*") { Merge-Hcon @{ $key = $value } $result } else { $pair = $value $segments = $key -split "\." for ($index = $segments.Count - 1; $index -ge 0; $index--) { $pair = @{ $segments[$index] = $pair } } Merge-Hcon $pair $result } } $result } } <# .SYNOPSIS Deep-merges a source hash table into a target hash table. .INPUTS The source hash table. .OUTPUTS The target hash table. #> function Merge-Hcon { [CmdletBinding()] [OutputType([void])] param ( # The source hash table. [Parameter(Mandatory, Position = 1, ValueFromPipeline)] [hashtable] $Source, # The target hash table. [Parameter(Mandatory, Position = 2)] [hashtable] $Target ) process { foreach ($key in $Source.Keys) { $value = $Source[$key] if (($value -is [hashtable]) -and ($Target[$key] -is [hashtable])) { Merge-Hcon $value $Target[$key] } else { $Target[$key] = $value } } } } |