jqfunc

package module
v0.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 18, 2026 License: BSD-2-Clause Imports: 9 Imported by: 1

README

HCL JQ Functions

A HashiCorp Configuration Language (HCL) extension that enables user-defined functions written in JQ. This package extends HCL with powerful data transformation capabilities using the JQ query language.

Features

  • JQ Integration: Write HCL functions using JQ query syntax
  • Flexible Input Types: Accept both JSON strings and native cty values
  • Smart String Handling: String results from JSON input are returned directly (not JSON-encoded). You can always use tojson in your query if you really want a string containing the quotes.
  • Multi-Result Support: Handle JQ queries that return multiple values
  • Parameter Support: Pass HCL function arguments as JQ variables
  • Enhanced Error Reporting: Detailed error messages with source location information

Installation

go get github.com/tsarna/hcl-jqfunc

Quick Start

Basic Usage
package main

import (
    "fmt"
    "log"
    
    "github.com/hashicorp/hcl/v2/hclparse"
    "github.com/tsarna/hcl-jqfunc"
    "github.com/zclconf/go-cty/cty"
)

func main() {
    // Define HCL with JQ functions
    hclCode := `
    jq "get_name" {
        params = []
        query = ".name"
    }
    
    jq "add_tax" {
        params = [rate]
        query = ".price * (1 + $rate)"
    }
    `
    
    // Parse and decode functions
    parser := hclparse.NewParser()
    file, diags := parser.ParseHCL([]byte(hclCode), "config.hcl")
    if diags.HasErrors() {
        log.Fatal(diags)
    }
    
    functions, _, diags := jqfunc.DecodeJqFunctions(file.Body, "jq")
    if diags.HasErrors() {
        log.Fatal(diags)
    }
    
    // Use the functions
    getName := functions["get_name"]
    result, _ := getName.Call([]cty.Value{
        cty.StringVal(`{"name": "Alice", "age": 30}`),
    })
    fmt.Println(result.AsString()) // Output: Alice
    
    addTax := functions["add_tax"]
    result, _ = addTax.Call([]cty.Value{
        cty.StringVal(`{"price": 100}`),
        cty.NumberFloatVal(0.08),
    })
    fmt.Println(result.AsString()) // Output: 108
}
HCL Function Definition Syntax
jq "function_name" {
    params = [param1, param2, ...]  # Parameter names (bare identifiers)
    query = "JQ_QUERY_STRING"       # JQ query with $param1, $param2, etc.
}

Examples

Data Extraction
jq "extract_user_info" {
    params = []
    query = "{name: .name, email: .contact.email}"
}
// JSON string input
result, _ := extractUserInfo.Call([]cty.Value{
    cty.StringVal(`{"name": "Bob", "contact": {"email": "bob@example.com"}}`),
})
// Returns: {"email":"bob@example.com","name":"Bob"}

// cty object input  
result, _ := extractUserInfo.Call([]cty.Value{
    cty.ObjectVal(map[string]cty.Value{
        "name": cty.StringVal("Bob"),
        "contact": cty.ObjectVal(map[string]cty.Value{
            "email": cty.StringVal("bob@example.com"),
        }),
    }),
})
// Returns: cty.ObjectVal with name and email fields
Array Processing
jq "filter_active_users" {
    params = []
    query = "[.users[] | select(.active == true) | .name]"
}
Parameterized Queries
jq "calculate_total" {
    params = [tax_rate, discount]
    query = ".price * (1 + $tax_rate) * (1 - $discount)"
}

Documentation

Function Definition
Block Syntax
  • Block Type: Use any name (commonly jq)
  • Function Name: Single label after block type
  • Parameters: List of bare identifiers in params attribute
  • Query: JQ query string in query attribute
Parameter Handling
  • Parameters become JQ variables prefixed with $
  • Example: params = [rate, discount] creates $rate and $discount variables
  • Parameters can be any cty type and are converted to Go values for JQ processing
Input and Output Behavior
Input Types
  1. JSON String: Parsed as JSON, processed by JQ, result handling depends on output type
  2. cty Value: Converted to Go value, processed by JQ, converted back to cty
Output Behavior by Input Type
Input Type Result Type Output
JSON String String Direct string (no JSON encoding)
JSON String Number/Object/Array JSON-encoded string
cty Value Any Corresponding cty type
Multi-Result Handling
  • Single result: Returned directly
  • Multiple results: Returned as array/list
  • No results: Returns null (JSON string) or cty.NullVal (cty input)
Error Handling

The package provides enhanced error reporting with:

  • Source Location: Exact HCL block location for compilation errors
  • Runtime Context: Function name and query for execution errors
  • Error Chaining: Go 1.13+ error unwrapping support
if err != nil {
    var jqErr *jqfunc.JqExecutionError
    if errors.As(err, &jqErr) {
        fmt.Printf("JQ error in %s at %s: %v\n", 
            jqErr.FunctionName, jqErr.Range, jqErr.Cause)
    }
}
Advanced Features
Custom Block Types
// Use custom block type instead of "jq"
functions, _, diags := jqfunc.DecodeJqFunctions(body, "transform")
Complex Data Transformations
jq "process_orders" {
    params = [min_amount]
    query = """
    [.orders[] | 
     select(.total >= $min_amount) | 
     {
       id: .id,
       customer: .customer.name,
       total: .total,
       items: [.items[].name]
     }
    ]
    """
}
Conditional Logic
jq "categorize_user" {
    params = []
    query = """
    if .age < 18 then "minor"
    elif .age < 65 then "adult"  
    else "senior"
    end
    """
}
Integration with HCL

JQ functions integrate seamlessly with HCL's function system:

# In your HCL configuration
locals {
    user_data = jsondecode(file("users.json"))
    active_users = filter_active_users(user_data)
    user_names = [for user in active_users : user.name]
}
Performance Considerations
  • JQ queries are compiled once during function creation
  • Parameter conversion happens at runtime
  • For high-frequency use, prefer cty input over JSON strings
  • Complex queries may benefit from breaking into smaller functions
Limitations
  • JQ variables must be prefixed with $ in queries
  • Queries have no access to variables in the execution context, only the passed in variables.
  • Queries cannot call other queries or HCL functions.
  • Parameter names must be valid HCL identifiers
  • JSON string input/output adds serialization overhead
  • Multi-result queries with mixed types may require careful handling

Dependencies

  • github.com/hashicorp/hcl/v2 - HCL parsing and evaluation
  • github.com/itchyny/gojq - Pure Go JQ implementation
  • github.com/zclconf/go-cty - HCL type system
  • github.com/tsarna/go2cty2go - Enhanced cty value conversion

Acknowledgments

Special thanks to itchyny for creating and maintaining gojq, the excellent pure Go implementation of JQ that makes this project possible. The gojq library provides the robust JQ query processing that powers all the functionality in this package.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DecodeJqFunctions

func DecodeJqFunctions(body hcl.Body, blockType string) (map[string]function.Function, hcl.Body, hcl.Diagnostics)

DecodeJqFunctions extracts and compiles jq function blocks from HCL bodies, returning HCL functions Similar to userfunc.DecodeUserFunctions but for jq functions

Types

type JqExecutionError

type JqExecutionError struct {
	FunctionName string
	Query        string
	Range        hcl.Range
	Cause        error
}

JqExecutionError wraps execution errors with source location information

func (*JqExecutionError) Error

func (e *JqExecutionError) Error() string

func (*JqExecutionError) Unwrap

func (e *JqExecutionError) Unwrap() error

type JqFunction

type JqFunction struct {
	Name          string
	Params        []string
	Query         string
	CompiledQuery *gojq.Code
	Range         hcl.Range // For error reporting
}

JqFunction represents a compiled jq function ready for execution

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL