[CmdletBinding()] param( [string[]]$Path = @( 'crates/common/src/formats', 'crates/toon/src', 'crates/ison/src', 'crates/isonl/src', 'crates/zon/src', 'crates/tonl/src' ) ) $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest $workspaceRoot = Split-Path -Parent $PSScriptRoot Push-Location -LiteralPath $workspaceRoot function Get-BraceDelta { param( [AllowEmptyString()] [Parameter(Mandatory = $true)] [string]$Line ) $open = ([regex]::Matches($Line, '\{')).Count $close = ([regex]::Matches($Line, '\}')).Count return $open - $close } function Test-AllowedLine { param( [AllowEmptyString()] [Parameter(Mandatory = $true)] [string]$Line, [AllowEmptyString()] [string]$PreviousLine ) $sameLineComment = $Line -match '(^|\s)//\s*jade:\s*allow-panic\s+because:\s+\S' $previousLineComment = -not [string]::IsNullOrWhiteSpace($PreviousLine) -and $PreviousLine -match '^\s*//\s*jade:\s*allow-panic\s+because:\s+\S' return $sameLineComment -or ( -not [string]::IsNullOrWhiteSpace($PreviousLine) -and $previousLineComment ) } $patterns = @( 'panic!\s*\(', '\.unwrap\s*\(', '\.expect\s*\(' ) $failures = [System.Collections.Generic.List[string]]::new() try { foreach ($root in $Path) { if (-not (Test-Path -LiteralPath $root)) { throw "No-panic scan path does not exist: $root" } $files = Get-ChildItem -LiteralPath $root -Recurse -File -Filter '*.rs' foreach ($file in $files) { if (($file.FullName -split '[\\/]') -contains 'tests') { continue } $lines = Get-Content -LiteralPath $file.FullName $pendingTestModule = $false $insideTestModule = $false $braceDepth = 0 $previousLine = '' for ($index = 0; $index -lt $lines.Count; $index += 1) { $line = [string]$lines[$index] $trimmed = $line.TrimStart() if ($insideTestModule) { $braceDepth += Get-BraceDelta -Line $line if ($braceDepth -le 0) { $insideTestModule = $false $braceDepth = 0 } $previousLine = $line continue } if ($trimmed.StartsWith('#[cfg(test)]')) { $pendingTestModule = $true $previousLine = $line continue } if ($pendingTestModule -and $trimmed -match '^mod\s+tests\s*\{') { $insideTestModule = $true $braceDepth = Get-BraceDelta -Line $line $pendingTestModule = $false $previousLine = $line continue } if (-not $trimmed.StartsWith('#[')) { $pendingTestModule = $false } if ($trimmed.StartsWith('//')) { $previousLine = $line continue } foreach ($pattern in $patterns) { if ($line -match $pattern -and -not (Test-AllowedLine -Line $line -PreviousLine $previousLine)) { $relative = Resolve-Path -LiteralPath $file.FullName -Relative $failures.Add("${relative}:$($index + 1): panic surface requires removal, fallible propagation, or a strict '// jade: allow-panic because: ' comment directly beside this call") } } $previousLine = $line } } } if ($failures.Count -gt 0) { $failures | ForEach-Object { Write-Error $_ } throw "No-panic gate found $($failures.Count) unapproved panic surface(s)." } Write-Host "No-panic gate passed for $($Path.Count) root(s)." } finally { Pop-Location }