Skip to content

Commit b257676

Browse files
🩹 [Patch]: Ensure all other value types is converted to string in Format-Hashtable (#12)
## Description This pull request includes several key improvements and bug fixes to the `Format-Hashtable` function. Enhancements to data type handling: * Improved handling of `PSCustomObject` by converting it to a hashtable and recursively formatting it. * Added support for `SwitchParameter` type alongside `bool`, ensuring proper conversion and formatting. * Enhanced handling of numeric types by including `long` and `decimal` in addition to `int` and `double`. * Refined array handling to support any `IList` implementation, covering normal arrays, `ArrayList`, `List<T>`, etc. Code readability improvements: * Added comments to explain the purpose of specific code blocks, such as nested hashtable handling and fallback string escaping. * Streamlined `foreach` loop for array elements, improving verbosity and element processing. ## Type of change <!-- Use the check-boxes [x] on the options that are relevant. --> - [ ] 📖 [Docs] - [ ] 🪲 [Fix] - [x] 🩹 [Patch] - [ ] ⚠️ [Security fix] - [ ] 🚀 [Feature] - [ ] 🌟 [Breaking change] ## Checklist <!-- Use the check-boxes [x] on the options that are relevant. --> - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas
1 parent 6bab12d commit b257676

2 files changed

Lines changed: 58 additions & 29 deletions

File tree

src/functions/public/Format-Hashtable.ps1

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -88,50 +88,62 @@
8888
continue
8989
}
9090
Write-Verbose "Value type: [$($value.GetType().Name)]"
91-
if (($value -is [System.Collections.IDictionary])) {
91+
if ($value -is [System.Collections.IDictionary]) {
92+
# Nested hashtable
9293
$nestedString = Format-Hashtable -Hashtable $value -IndentLevel ($IndentLevel + 1)
9394
$lines += "$levelIndent$paddedKey = $nestedString"
9495
} elseif ($value -is [System.Management.Automation.PSCustomObject]) {
96+
# PSCustomObject => Convert to hashtable & recurse
9597
$nestedString = $value | ConvertTo-Hashtable | Format-Hashtable -IndentLevel ($IndentLevel + 1)
9698
$lines += "$levelIndent$paddedKey = $nestedString"
97-
} elseif ($value -is [System.Management.Automation.PSObject]) {
98-
$nestedString = $value | ConvertTo-Hashtable | Format-Hashtable -IndentLevel ($IndentLevel + 1)
99-
$lines += "$levelIndent$paddedKey = $nestedString"
100-
} elseif ($value -is [bool]) {
101-
$lines += "$levelIndent$paddedKey = `$$($value.ToString().ToLower())"
102-
} elseif ($value -is [int] -or $value -is [double]) {
99+
} elseif ( $value -is [bool] -or $value -is [System.Management.Automation.SwitchParameter] ) {
100+
$boolValue = [bool]$value
101+
$lines += "$levelIndent$paddedKey = `$$($boolValue.ToString().ToLower())"
102+
} elseif ($value -is [int] -or $value -is [long] -or $value -is [double] -or $value -is [decimal]) {
103103
$lines += "$levelIndent$paddedKey = $value"
104-
} elseif ($value -is [array]) {
104+
} elseif ($value -is [System.Collections.IList]) {
105+
# This covers normal arrays, ArrayList, List<T>, etc.
105106
if ($value.Count -eq 0) {
106107
$lines += "$levelIndent$paddedKey = @()"
107108
} else {
108109
$lines += "$levelIndent$paddedKey = @("
109-
$arrayIndent = $levelIndent + $indent # Increase indentation for elements inside @(...)
110+
$arrayIndent = $levelIndent + $indent
110111

111-
$value | ForEach-Object {
112-
$nestedValue = $_
113-
Write-Verbose "Processing array element: $_"
114-
Write-Verbose "Element type: $($_.GetType().Name)"
115-
if (($nestedValue -is [System.Collections.Hashtable]) -or ($nestedValue -is [System.Collections.Specialized.OrderedDictionary])) {
112+
foreach ($nestedValue in $value) {
113+
Write-Verbose "Processing array element: [$nestedValue]"
114+
Write-Verbose "Element type: [$($nestedValue.GetType().Name)]"
115+
116+
if (($nestedValue -is [System.Collections.IDictionary])) {
117+
# Nested hashtable
116118
$nestedString = Format-Hashtable -Hashtable $nestedValue -IndentLevel ($IndentLevel + 2)
117119
$lines += "$arrayIndent$nestedString"
118-
} elseif ($nestedValue -is [bool]) {
119-
$lines += "$arrayIndent`$$($nestedValue.ToString().ToLower())"
120-
} elseif ($nestedValue -is [int]) {
120+
} elseif ($nestedValue -is [System.Management.Automation.PSCustomObject]) {
121+
# PSCustomObject => Convert to hashtable & recurse
122+
$nestedString = $nestedValue | ConvertTo-Hashtable | Format-Hashtable -IndentLevel ($IndentLevel + 2)
123+
$lines += "$arrayIndent$nestedString"
124+
} elseif ( $nestedValue -is [bool] -or $nestedValue -is [System.Management.Automation.SwitchParameter] ) {
125+
$boolValue = [bool]$nestedValue
126+
$lines += "$arrayIndent`$$($boolValue.ToString().ToLower())"
127+
} elseif ($nestedValue -is [int] -or $nestedValue -is [long] -or $nestedValue -is [double] -or $nestedValue -is [decimal]) {
121128
$lines += "$arrayIndent$nestedValue"
122129
} else {
123-
$lines += "$arrayIndent'$nestedValue'"
130+
# Fallback => treat as string (escape single-quotes)
131+
$escapedElement = $nestedValue -replace "('+)", "''"
132+
$lines += "$arrayIndent'$escapedElement'"
124133
}
125134
}
126-
$arrayIndent = $levelIndent
127-
$lines += "$arrayIndent)"
135+
136+
$lines += ($levelIndent + ')')
128137
}
129138
} else {
130-
$value = $value -replace "('+)", "''" # Escape single quotes in a manifest file
131-
$lines += "$levelIndent$paddedKey = '$value'"
139+
# Fallback: treat as string (escaping single-quotes)
140+
$escapedValue = $value -replace "('+)", "''"
141+
$lines += "$levelIndent$paddedKey = '$escapedValue'"
132142
}
133143
}
144+
134145
$levelIndent = $indent * ($IndentLevel - 1)
135146
$lines += "$levelIndent}"
147+
136148
return $lines -join [Environment]::NewLine
137149
}

tests/Hashtable.Tests.ps1

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -413,9 +413,17 @@
413413
AnArrayOfHashtables = @(
414414
[ordered]@{
415415
Path = 'C:\Repos\GitHub\PSModule\Action\Invoke-Pester\tests\3-Advanced\Planets\Planets.Tests.ps1'
416-
Data = [pscustomobject]@{
417-
Path = 'C:\Repos\GitHub\PSModule\Action\Invoke-Pester\tests\3-Advanced\Planets\Planets.Data.ps1'
418-
}
416+
Data = @(
417+
[pscustomobject]@{
418+
Path = 'C:\Repos\GitHub\PSModule\Action\Invoke-Pester\tests\3-Advanced\Planets\Planets.Data.ps1'
419+
},
420+
[pscustomobject]@{
421+
Path = 'C:\Repos\GitHub\PSModule\Action\Invoke-Pester\tests\3-Advanced\Planets\Planets.Data.ps1'
422+
},
423+
[pscustomobject]@{
424+
Path = 'C:\Repos\GitHub\PSModule\Action\Invoke-Pester\tests\3-Advanced\Planets\Planets.Data.ps1'
425+
}
426+
)
419427
},
420428
[ordered]@{
421429
Path = 'C:\Repos\GitHub\PSModule\Action\Invoke-Pester\tests\3-Advanced\Sheep\Sheep.Tests.ps1'
@@ -429,7 +437,7 @@
429437

430438
# Act - Run the function
431439
$formatted = Format-Hashtable -Hashtable $testHashtable
432-
440+
Write-Verbose $formatted -Verbose
433441
# Assert - Define the expected output
434442
$expectedOutput = @'
435443
@{
@@ -479,9 +487,17 @@
479487
AnArrayOfHashtables = @(
480488
@{
481489
Path = 'C:\Repos\GitHub\PSModule\Action\Invoke-Pester\tests\3-Advanced\Planets\Planets.Tests.ps1'
482-
Data = @{
483-
Path = 'C:\Repos\GitHub\PSModule\Action\Invoke-Pester\tests\3-Advanced\Planets\Planets.Data.ps1'
484-
}
490+
Data = @(
491+
@{
492+
Path = 'C:\Repos\GitHub\PSModule\Action\Invoke-Pester\tests\3-Advanced\Planets\Planets.Data.ps1'
493+
}
494+
@{
495+
Path = 'C:\Repos\GitHub\PSModule\Action\Invoke-Pester\tests\3-Advanced\Planets\Planets.Data.ps1'
496+
}
497+
@{
498+
Path = 'C:\Repos\GitHub\PSModule\Action\Invoke-Pester\tests\3-Advanced\Planets\Planets.Data.ps1'
499+
}
500+
)
485501
}
486502
@{
487503
Path = 'C:\Repos\GitHub\PSModule\Action\Invoke-Pester\tests\3-Advanced\Sheep\Sheep.Tests.ps1'
@@ -494,6 +510,7 @@
494510
}
495511
496512
'@.Trim() # Trim to remove any unintended whitespace
513+
Write-Verbose $expectedOutput -Verbose
497514

498515
# Compare function output to expected output
499516
$formatted | Should -BeExactly $expectedOutput

0 commit comments

Comments
 (0)