TerraformAST.psm1

# Load the Go DLL using P/Invoke
$dllPath = Join-Path $PSScriptRoot "hcl_parser.dll"
if (-not (Test-Path $dllPath)) {
    throw "DLL not found: $dllPath"
}

# Escape backslashes in the file path
$escapedDllPath = $dllPath -replace '\\', '\\'

# Define the signatures of the Go functions
$signature = @"
    [DllImport("$escapedDllPath", CharSet = CharSet.Ansi)]
    public static extern IntPtr ParseHCL(string filePath);
 
    [DllImport("$escapedDllPath", CharSet = CharSet.Ansi)]
    public static extern void FreeString(IntPtr str);
"@


try {
    # Add the type definition
    Add-Type -MemberDefinition $signature -Name HCLParser -Namespace Go
} catch {
    throw "Failed to define Go function: $_"
}

function Get-TerraformAST {
    [CmdletBinding()]
    Param (
        [Parameter(
            Mandatory,
            ValueFromPipeline,
            ValueFromPipelineByPropertyName,
            HelpMessage = "The path to the Terraform file to parse."
        )]
        [Alias("FullName")]
        [String]
        $Path
    )

    BEGIN {

        Write-Verbose "[ $($MyInvocation.InvocationName) ] Executing"

        if (-not(Get-Command -Name terraform.exe -ErrorAction SilentlyContinue)) {
            Write-Error "Terraform is not installed!"
            return
        }

    }

    PROCESS {

        if (-not(Test-Path -Path $Path -PathType Leaf)) {
            Write-Error "The -Path $($Path) does not exist!"
            return
        }

        if (-not(([System.IO.FileInfo]$Path).Extension -eq ".tf")) {
            Write-Error "The -Path $($Path) file extension is not .tf"
            return
        }

        # Resolve the absolute path (cross-platform)

        $absPath = try {

            Resolve-Path $Path -ErrorAction Stop | Select-Object -ExpandProperty Path

        } catch {

            Write-Error $_.Exception.Message
            return

        }

        Write-Verbose " - $($absPath)"
        
        # Call the Go function to parse the HCL file
        
        try {

            $astJsonPtr = [Go.HCLParser]::ParseHCL($absPath)
            if ($astJsonPtr -eq [IntPtr]::Zero) {
                throw "Failed to parse HCL file: Null pointer returned"
            }

            # Convert the returned pointer to a PowerShell string
            $astJson = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($astJsonPtr)

            if (-not $astJson) {
                throw "Failed to convert AST JSON to string"
            }

            # Free the memory allocated by Go
            [Go.HCLParser]::FreeString($astJsonPtr)

            # Convert the JSON string to a PowerShell object and pretty-print it

            $ast = try {

                if ($PSVersionTable.PSVersion.Major -gt 5) {
                    $astJson | ConvertFrom-Json -Depth 99 -ErrorAction Stop
                } else {
                    $astJson | ConvertFrom-Json -ErrorAction Stop
                }
                
            } catch {
                Write-Error $_.Exception.Message
                return
            }

            $ast.Body.Blocks | ForEach-Object {
                $_
            }

        } catch {
            throw "Error in Get-TerraformAST: $_"
        }

    }

    END {

        Write-Verbose "[ $($MyInvocation.InvocationName) ] Completed"

    }

}