regobrick

package module
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Dec 14, 2025 License: Apache-2.0 Imports: 13 Imported by: 0

README

RegoBrick

RegoBrick provides a straightforward way to parse and transform Rego modules without modifying the OPA engine. It applies certain transformations based on special import markers (for example, import data.regobrick.default_false) and also offers convenient helpers for custom builtins and Go↔Rego value conversion.

Number Type

regobrick.Number is an alias for json.Number, used to pass numeric values to Rego without floating-point precision loss.

input := map[string]any{
    "price":    regobrick.Number("123.45"),
    "quantity": regobrick.Number("10"),
}

Contract:

  • Exponent notation (1e-8, 2.5E10) is not supported
  • If exponent notation is used with UseDecimalArithmetic():
    • Default mode: operation silently fails (rule not satisfied, no result)
    • StrictBuiltinErrors(true): returns eval_builtin_error
  • Input validation is the caller's responsibility

Precision Limits (udecimal):

  • Maximum 19 decimal places
  • Range: ±34,028,236,692,093,846,346.3374607431768211455
  • Exceeding 19 decimal places results in truncation (not rounding)
  • Sufficient for: BTC (8 decimals), ETH (18 decimals), fiat currencies

Overview

  • Default False If your Rego module imports data.regobrick.default_false, RegoBrick will automatically insert a default rule that evaluates to false for any "if" or boolean rules. This helps ensure you don't forget to explicitly set them to false when not satisfied.

  • Custom Builtins Easily register builtins with typed arguments and return values. RegoBrick converts Rego AST terms to Go types and back, so you can write builtins in Go with minimal boilerplate.

  • Operator Overloading Optionally override Rego's arithmetic and comparison operators with precision decimal operations.

Installation

go get github.com/sky1core/regobrick

Make sure you also have OPA in your go.mod if you plan to work with the Rego engine.

Usage

Below is an example of how to use RegoBrick with Number input data. By including import data.regobrick.default_false in your policy, RegoBrick automatically inserts a default rule (for example, default allow = false), ensuring that if the condition isn't met, the rule defaults to false.

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/open-policy-agent/opa/v1/rego"
    "github.com/sky1core/regobrick"
)

func main() {
    ctx := context.Background()

    // Example policy for the "sub" package
    subPolicy := `
        package sub

        some_rule {
            input.amount == 123.45
        }
    `
    // Example policy for the "main" package
    mainPolicy := `
        package example

        import data.regobrick.default_false

        allow if {
            input.user == "admin"
        }
    `

    // Build a rego.Rego object with your modules and input.
    query, err := rego.New(
        // Add Rego modules (which will apply "default_false" if that import is found):
        regobrick.Module("sub.rego", subPolicy, []string{"data.some.pkg"}),
        regobrick.Module("main.rego", mainPolicy, []string{"data.mycompany.util"}),

        // Specify the query we want to evaluate:
        rego.Query("data.example.allow"),

    ).PrepareForEval(ctx)

    if err != nil {
        log.Fatal(err)
    }

    // Build the input map with Number values to avoid floating-point issues.
    input := map[string]any{
        "user":   "admin",
        "amount": regobrick.Number("123.45"),
    }

    // Evaluate using rego.EvalInput to pass input.
    rs, err := query.Eval(ctx, rego.EvalInput(input))
    if err != nil {
        log.Fatal(err)
    }

    // The result of 'data.example.allow' is in rs.
    // Because 'allow if ...' is accompanied by 'default allow = false',
    // if the condition is not met, it defaults to false.
    fmt.Println("Result:", rs)
}

Precision Arithmetic

RegoBrick provides operator overloading for precision arithmetic using udecimal internally. Call UseDecimalArithmetic() once at startup to replace Rego's default float-based operators.

func init() {
    regobrick.UseDecimalArithmetic()
}

This overloads:

  • Arithmetic: +, -, *, /, %
  • Comparison: >, >=, <, <=, ==, !=
  • Unary: abs(), round(), ceil(), floor()

Notes:

  • On error (e.g., divide by zero, invalid number format):
    • Default mode: operation silently fails (rule not satisfied)
    • StrictBuiltinErrors(true): returns eval_builtin_error

Writing Custom Builtins

You can register a custom function that OPA calls within your policies. RegoBrick provides helper functions (like RegisterBuiltin1, RegisterBuiltin2, etc.) for builtins that accept typed Go arguments and return typed Go values.

package main

import (
    "github.com/open-policy-agent/opa/v1/rego"
    "github.com/sky1core/regobrick"
)

// Example builtin that checks if a user is "admin"
func isAdmin(ctx rego.BuiltinContext, user string) (bool, error) {
    return user == "admin", nil
}

func init() {
    regobrick.RegisterBuiltin1[string, bool](
        "is_admin",
        isAdmin,
        regobrick.WithCategories("my_custom_category"),
    )
}
Memoization with WithMemoize

For expensive computations, use WithMemoize() to cache results for the same arguments within a single evaluation:

regobrick.RegisterBuiltin1[string, int](
    "expensive_lookup",
    expensiveLookup,
    regobrick.WithMemoize(),
)
Advanced Options with ConfigureFunction

For advanced use cases not covered by built-in options, use ConfigureFunction to directly configure the underlying rego.Function:

regobrick.RegisterBuiltin1[string, int](
    "custom_func",
    customFunc,
    regobrick.ConfigureFunction(func(f *rego.Function) {
        f.Memoize = true
        f.Nondeterministic = true
    }),
)

Filtering Builtins with FilterCapabilities

If you want to restrict which builtins are allowed when evaluating a policy, you can use the FilterCapabilities function to include or exclude builtins by name and category.

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/open-policy-agent/opa/v1/rego"
    "github.com/sky1core/regobrick"
)

func main() {
    allowedNames := []string{"is_admin", "concat"}
    allowedCats := []string{"my_custom_category", "strings"}

    caps := regobrick.FilterCapabilities(allowedNames, allowedCats)

    ctx := context.Background()
    query, err := rego.New(
        rego.Query("data.example.allow"),
        rego.Capabilities(caps),
    ).PrepareForEval(ctx)
    if err != nil {
        log.Fatal(err)
    }

    rs, err := query.Eval(ctx)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Query result:", rs)
}

Documentation

Overview

Package regobrick provides high-level functions for applying RegoBrick transformations and adding modules to OPA rego.Rego objects.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FilterCapabilities added in v0.3.0

func FilterCapabilities(allowedNames []string, allowedCats []string) *ast.Capabilities

FilterCapabilities filters OPA built-in functions based on a list of allowed names and categories, along with built-ins whose infix is in coreInfixes.

  • If a built-in's infix is one of the core infixes, it is kept.
  • Otherwise, if the built-in name is in allowedNames or any of its categories are in allowedCats, it is kept.
  • If a built-in has custom category mappings, those are also checked against allowedCats.

The resulting capabilities object contains only the filtered built-ins.

func Module

func Module(filename, src string, imports []string) func(*rego.Rego)

Module returns a rego.Rego option that adds a single Rego module from the given filename, source, and optional imports. If you need to handle parse errors directly, parse the module yourself (for example, with regobrick.ParseModule or ast.ParseModule) and then pass the *ast.Module to rego.ParsedModule(...) in your own Rego configuration.

func Modules added in v0.5.0

func Modules(opts ...ModuleOption) func(*rego.Rego)

Modules returns a rego.Rego option that adds multiple Rego modules in one call, each specified via a ModuleOption.

func ParseModule

func ParseModule(filename, src string, imports []string) (*ast.Module, error)

ParseModule parses a Rego source file into an AST module, optionally appending additional imports. If the module includes "import data.regobrick.default_false", it applies the default_false transform.

func RegisterBuiltin0 added in v0.2.0

func RegisterBuiltin0[R any](name string, fn func(rego.BuiltinContext) (R, error), opts ...BuiltinRegisterOption)

RegisterBuiltin0 registers a builtin with no arguments. Must be called during package initialization (init function). Calling after initialization may cause race conditions.

func RegisterBuiltin0_ added in v0.2.0

func RegisterBuiltin0_(name string, fn func(rego.BuiltinContext) error, opts ...BuiltinRegisterOption)

RegisterBuiltin0_ registers a builtin with no arguments that returns only an error (null to Rego). Must be called during package initialization (init function). Calling after initialization may cause race conditions.

func RegisterBuiltin1 added in v0.2.0

func RegisterBuiltin1[T1 any, R any](name string, fn func(rego.BuiltinContext, T1) (R, error), opts ...BuiltinRegisterOption)

RegisterBuiltin1 registers a builtin with 1 argument. Must be called during package initialization (init function). Calling after initialization may cause race conditions.

func RegisterBuiltin1_ added in v0.2.0

func RegisterBuiltin1_[T1 any](name string, fn func(rego.BuiltinContext, T1) error, opts ...BuiltinRegisterOption)

RegisterBuiltin1_ registers a builtin with 1 argument that returns only an error (null to Rego). Must be called during package initialization (init function). Calling after initialization may cause race conditions.

func RegisterBuiltin2 added in v0.2.0

func RegisterBuiltin2[T1 any, T2 any, R any](name string, fn func(rego.BuiltinContext, T1, T2) (R, error), opts ...BuiltinRegisterOption)

RegisterBuiltin2 registers a builtin with 2 arguments. Must be called during package initialization (init function). Calling after initialization may cause race conditions.

func RegisterBuiltin2_ added in v0.2.0

func RegisterBuiltin2_[T1 any, T2 any](name string, fn func(rego.BuiltinContext, T1, T2) error, opts ...BuiltinRegisterOption)

RegisterBuiltin2_ registers a builtin with 2 arguments that returns only an error (null to Rego). Must be called during package initialization (init function). Calling after initialization may cause race conditions.

func RegisterBuiltin3 added in v0.2.0

func RegisterBuiltin3[T1 any, T2 any, T3 any, R any](name string, fn func(rego.BuiltinContext, T1, T2, T3) (R, error), opts ...BuiltinRegisterOption)

RegisterBuiltin3 registers a builtin with 3 arguments. Must be called during package initialization (init function). Calling after initialization may cause race conditions.

func RegisterBuiltin3_ added in v0.2.0

func RegisterBuiltin3_[T1 any, T2 any, T3 any](name string, fn func(rego.BuiltinContext, T1, T2, T3) error, opts ...BuiltinRegisterOption)

RegisterBuiltin3_ registers a builtin with 3 arguments that returns only an error (null to Rego). Must be called during package initialization (init function). Calling after initialization may cause race conditions.

func RegisterBuiltin4 added in v0.2.0

func RegisterBuiltin4[T1 any, T2 any, T3 any, T4 any, R any](name string, fn func(rego.BuiltinContext, T1, T2, T3, T4) (R, error), opts ...BuiltinRegisterOption)

RegisterBuiltin4 registers a builtin with 4 arguments. Must be called during package initialization (init function). Calling after initialization may cause race conditions.

func RegisterBuiltin4_ added in v0.2.0

func RegisterBuiltin4_[T1 any, T2 any, T3 any, T4 any](name string, fn func(rego.BuiltinContext, T1, T2, T3, T4) error, opts ...BuiltinRegisterOption)

RegisterBuiltin4_ registers a builtin with 4 arguments that returns only an error (null to Rego). Must be called during package initialization (init function). Calling after initialization may cause race conditions.

func RegisterBuiltin5 added in v0.2.0

func RegisterBuiltin5[T1 any, T2 any, T3 any, T4 any, T5 any, R any](name string, fn func(rego.BuiltinContext, T1, T2, T3, T4, T5) (R, error), opts ...BuiltinRegisterOption)

RegisterBuiltin5 registers a builtin with 5 arguments. Must be called during package initialization (init function). Calling after initialization may cause race conditions.

func RegisterBuiltin5_ added in v0.2.0

func RegisterBuiltin5_[T1 any, T2 any, T3 any, T4 any, T5 any](name string, fn func(rego.BuiltinContext, T1, T2, T3, T4, T5) error, opts ...BuiltinRegisterOption)

RegisterBuiltin5_ registers a builtin with 5 arguments that returns only an error (null to Rego). Must be called during package initialization (init function). Calling after initialization may cause race conditions.

func UseDecimalArithmetic added in v0.7.0

func UseDecimalArithmetic()

UseDecimalArithmetic Rego의 숫자 연산을 정밀 decimal 연산으로 대체합니다.

오버로딩되는 연산자:

  • 산술: +, -, *, /, %
  • 비교: >, >=, <, <=, ==, !=
  • 단항: abs(), round(), ceil(), floor()
  • 집계: sum(), product(), max(), min()

정밀도 제한 (udecimal):

  • 소수점 이하 최대 19자리
  • 범위: ±34,028,236,692,093,846,346.3374607431768211455
  • 19자리 초과 시 truncate (반올림 아님)

이 함수를 호출하면 Rego에서 다음과 같이 자연스럽게 사용 가능:

reduce_price := entry_price * (1 + reduce_rate)
can_reduce := reduce_amt >= min_amount
rounded := round(price)
total := sum([price1, price2, price3])

Types

type BuiltinRegisterOption added in v0.4.0

type BuiltinRegisterOption func(*builtinRegisterConfig)

func ConfigureFunction added in v0.7.0

func ConfigureFunction(configurator func(*rego.Function)) BuiltinRegisterOption

ConfigureFunction allows direct configuration of the rego.Function before registration. Use this for advanced options not directly supported by other options. Passing nil is a no-op.

func WithCategories added in v0.4.0

func WithCategories(cats ...string) BuiltinRegisterOption

func WithMemoize added in v0.7.0

func WithMemoize() BuiltinRegisterOption

WithMemoize enables memoization for the builtin. Memoized builtins cache results for the same inputs within a single evaluation.

func WithNondeterministic added in v0.4.0

func WithNondeterministic() BuiltinRegisterOption

WithNondeterministic marks the builtin as nondeterministic. Nondeterministic builtins may return different results for the same inputs.

type ModuleOption added in v0.5.0

type ModuleOption = module.ModuleOption

ModuleOption is an alias for module.ModuleOption, used for specifying Rego module parameters or configuration.

type Number added in v0.7.0

type Number = json.Number

Number Rego에 전달하는 숫자 표현. json.Number의 alias로, JSON 직렬화 시 숫자 리터럴로 출력된다.

계약:

  • 지수표기(e/E)는 미지원. 지수표기가 들어오면 UseDecimalArithmetic()의 연산에서 udecimal 파싱 에러로 평가가 실패한다.
  • 입력값의 유효성은 제공자 책임. regobrick은 연산 시점에만 검증한다.

예시:

input := map[string]any{
    "price": regobrick.Number("123.45"),
}

type RegoDecimal deprecated added in v0.6.0

type RegoDecimal = types.RegoDecimal

RegoDecimal is an alias for types.RegoDecimal. It represents a numeric value that serializes to JSON as a numeric literal (e.g., 123.456) rather than a string (e.g., "123.456").

Deprecated: Use Number instead. RegoDecimal will be removed in a future version.

func NewRegoDecimal deprecated added in v0.6.0

func NewRegoDecimal(d decimal.Decimal) RegoDecimal

NewRegoDecimal creates a RegoDecimal from an existing decimal.Decimal value. The resulting RegoDecimal retains the same precision and scale.

Deprecated: Use Number(d.String()) instead.

func NewRegoDecimalFromInt deprecated added in v0.6.0

func NewRegoDecimalFromInt(i int64) RegoDecimal

NewRegoDecimalFromInt creates a RegoDecimal from an int64. This is a shortcut for decimal.NewFromInt(...) wrapped in a RegoDecimal.

Deprecated: Use Number(strconv.FormatInt(i, 10)) instead.

Directories

Path Synopsis
internal
module
Package module provides utilities for parsing and transforming Rego modules, particularly for detecting and applying regobrick features like "default_false".
Package module provides utilities for parsing and transforming Rego modules, particularly for detecting and applying regobrick features like "default_false".
types
Package types provides wrappers around shopspring/decimal.
Package types provides wrappers around shopspring/decimal.

Jump to

Keyboard shortcuts

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