internal/functions/Resolve-FunctionCode.ps1

function Resolve-FunctionCode {
<#
    .SYNOPSIS
        Resolves the function code to send to OpenAI based on the provided PowerShell AST object
 
    .DESCRIPTION
        Resolves the function code to send to OpenAI based on the provided PowerShell AST object.
        It ensures the total token count is below the limit given, truncating code parts as needed.
 
    .PARAMETER Ast
        The PowerShell AST object representing the function code.
 
    .PARAMETER TokenLimit
        The maximum token count to be allowed.
        Defaults to: 3000
 
    .EXAMPLE
        PS C:\> Resolve-FunctionCode -Ast $ast
 
        Returns the text value of the function AST provided, as should be sent to OpenAI
#>

    [OutputType([string])]
    [CmdletBinding()]
    param (
        [System.Management.Automation.Language.FunctionDefinitionAst]
        $Ast,

        [int]
        $TokenLimit = 3000
    )

    if ((Measure-TokenCount -Code $Ast.Extent.Text) -lt $TokenLimit) {
        return $Ast.Extent.Text | Set-String -OldValue '\s+' -NewValue ' '
    }

    $keyWord = 'function'
    if ($Ast.IsFilter) { $keyWord = 'filter' }
    if ($Ast.IsWorkflow) { $keyWord = 'workflow' }

    $textHeader = @(
        '{0} {1} {{' -f $keyWord, $Ast.Name
    ) + @(
        $Ast.Body.ParamBlock.Attributes.Extent.Text | Split-String "`n" | Set-String "^\s+" | Join-String "`n"
    ) + @(
        $Ast.Body.ParamBlock.Extent.Text | Split-String "`n" | Set-String "^\s+" | Join-String "`n"
    )

    $beginBlock = ''
    if ($Ast.Body.BeginBlock) {
        $beginBlock = @"
begin {
$($Ast.Body.BeginBlock.Statements.Extent.Text | Split-String "`n" | Set-String "^\s+" | Join-String "`n")
}
"@

    }
    $processBlock = ''
    if ($Ast.Body.ProcessBlock) {
        $processBlock = @"
process {
$($Ast.Body.ProcessBlock.Statements.Extent.Text | Split-String "`n" | Set-String "^\s+" | Join-String "`n")
}
"@

    }
    $endBlock = ''
    if ($Ast.Body.EndBlock) {
        $endBlock = @"
end {
$($Ast.Body.EndBlock.Statements.Extent.Text | Split-String "`n" | Set-String "^\s+" | Join-String "`n")
}
"@

    }

    $cases = @(
        @($textHeader) + @($beginBlock) + @($processBlock) + @($endBlock) + @('}') | Join-String "`n"
        @($textHeader) + @($beginBlock) + @($processBlock) + @('}') | Join-String "`n"
        @($textHeader) + @($processBlock) + @('}') | Join-String "`n"
    )

    foreach ($case in $cases) {
        if ((Measure-TokenCount -Code $case) -lt $TokenLimit) {
            return $case | Set-String -OldValue '\s+' -NewValue ' '
        }
    }

    $lines = $processBlock -split "`n"
    foreach ($index in 0..$lines.Count) {
        $newProcess = $lines[0..($lines.Count - 1 - $index)]
        $newText = @($textHeader) + @($newProcess) + @('}') | Join-String "`n"
        if ((Measure-TokenCount -Code $newText) -lt $TokenLimit) {
            return $newText | Set-String -OldValue '\s+' -NewValue ' '
        }
    }

    throw "Unable to generate a scanable function code with $TokenLimit tokens. This usually only happens when the param block is too large to accomodate anything else. Param block size: $($Ast.Body.ParamBlock.Extent.Text.Length)"
}