Package local

import "github.com/ornew/hff/backends/local"

Generated from this checkout's Go source. Released versions are also on pkg.go.dev.

Overview

Package local builds an in-process evaluator for an already compiled HFF Program. It evaluates records without database access or other I/O.

The usual workflow is engine.New, Engine.Compile, local.Build, then Filter.Match. Build checks the Program and backend configuration once and returns an immutable Filter that can match many records concurrently. Match accepts an explicit hff.Value object and validates it against the Program's schema before evaluating the filter.

Match returns (false, nil) for a completed non-match. Ordinary conversion or arithmetic failures inside a query become null according to HFF semantics; invalid host input, cancellation, and exhausted budgets return typed errors. Do not treat an error as a completed false result. MatchInput skips repeated input validation for an hff.Input with an exactly matching schema contract.

WithLimits configures input and runtime budgets; zero fields use resolved defaults and WithUnlimitedLimits disables those budgets explicitly. Compile limits belong to the Program and cannot be changed by local Build options. Named time zones require caller-supplied versioned TZif data through WithTimeZones; UTC and fixed offsets are always available.

Extensions are declared to the engine separately from local implementations. Pair a declaration with Implement and register it using WithMethods. The implementation receives a Call for cancellation and extra budget charging; it must be deterministic, side-effect-free, and safe for concurrent matches.

Index

Functions

func Profile

Source

func Profile() hff.Profile

Profile describes built-in support and the implementation's compatibility versions. It does not grant service permissions or load time-zone data.

Types

type Call

Source

type Call struct {
	// contains filtered or unexported fields
}

Call provides the invocation context and additional budget accounting to a local extension. Charge methods may be called concurrently while the invocation is active; do not retain Call or use it after the function returns.

func (*Call) ChargeGeneratedValues

Source

func (c *Call) ChargeGeneratedValues(n int) error

ChargeGeneratedValues accounts for values produced or traversed by variable extension work. A failed charge aborts the whole Match even if ignored.

func (*Call) ChargeSteps

Source

func (c *Call) ChargeSteps(n int) error

ChargeSteps accounts for additional implementation work beyond BaseCost. A negative charge or an exceeded limit fails the whole Match, even if the extension ignores the returned error.

func (*Call) Context

Source

func (c *Call) Context() context.Context

Context returns the context for this invocation, or nil for a nil Call.

type Config

Source

type Config struct {
	// Limits selects validation and evaluation ceilings. Zero fields resolve to
	// finite defaults after an explicit replacement. Without replacement, Build
	// starts with the Program's complete limits.
	Limits	hff.Limits
	// UnlimitedLimits explicitly disables limits selected by this configuration.
	UnlimitedLimits	bool
	// TimeZones supplies versioned TZif data for named zones. Build snapshots it.
	TimeZones	*TimeZoneProfile
	// Methods supplies local implementations for declared extension signatures.
	Methods	[]MethodImplementation
}

Config controls per-operation validation and evaluation limits. Build starts with the Program limits. An explicit Limits replacement uses finite defaults for zero fields; UnlimitedLimits is explicit.

type Filter

Source

type Filter struct {
	// contains filtered or unexported fields
}

Filter snapshots its configuration. Each call owns independent evaluation state.

func Build

Source

func Build(ctx context.Context, program hff.Program, options ...Option) (*Filter, error)

Build validates local support without evaluating a record and returns an immutable Filter safe for concurrent calls. It snapshots zone data and method implementations. Options apply left to right; later options replace earlier settings. Build performs no record validation or matching.

Example

{
	ctx := context.Background()
	e, err := engine.New(engine.WithSchema(schema.Struct(schema.Int("age"))),
		engine.WithCapabilities(capabilities.Standard()))
	if err != nil {
		panic(err)
	}
	program, err := e.Compile(ctx, `age>=18`)
	if err != nil {
		panic(err)
	}
	filter, err := local.Build(ctx, program)
	if err != nil {
		panic(err)
	}
	for _, age := range []int64{16, 25} {
		matched, err := filter.Match(ctx, hff.Object(map[string]hff.Value{"age": hff.Int(age)}))
		if err != nil {
			panic(err)
		}
		fmt.Println(age, matched)
	}

}
Complete runnable example
package main

import (
	"context"
	"fmt"

	"github.com/ornew/hff"
	"github.com/ornew/hff/backends/local"
	"github.com/ornew/hff/capabilities"
	"github.com/ornew/hff/engine"
	"github.com/ornew/hff/schema"
)

func main() {
	ctx := context.Background()
	e, err := engine.New(engine.WithSchema(schema.Struct(schema.Int("age"))),
		engine.WithCapabilities(capabilities.Standard()))
	if err != nil {
		panic(err)
	}
	program, err := e.Compile(ctx, `age>=18`)
	if err != nil {
		panic(err)
	}
	filter, err := local.Build(ctx, program)
	if err != nil {
		panic(err)
	}
	for _, age := range []int64{16, 25} {
		matched, err := filter.Match(ctx, hff.Object(map[string]hff.Value{"age": hff.Int(age)}))
		if err != nil {
			panic(err)
		}
		fmt.Println(age, matched)
	}
}

Output:

16 false
25 true

func (*Filter) Match

Source

func (f *Filter) Match(ctx context.Context, record hff.Value) (bool, error)

Match validates the complete record before evaluating the filter, including fields the query does not read. A completed null/no-value predicate is false; cancellation, invalid input, and exceeded limits are errors.

func (*Filter) MatchInput

Source

func (f *Filter) MatchInput(ctx context.Context, input hff.Input) (bool, error)

MatchInput evaluates an immutable Input without repeating its recursive validation. The Input must carry the exact Program schema contract; ordinary query work and runtime limits still apply.

Example

{
	ctx := context.Background()
	e, err := engine.New(engine.WithSchema(schema.Struct(schema.Int("age"))),
		engine.WithCapabilities(capabilities.Standard()))
	if err != nil {
		panic(err)
	}
	record := hff.Object(map[string]hff.Value{"age": hff.Int(25)})
	input, err := e.Validate(ctx, record)
	if err != nil {
		panic(err)
	}

	for _, source := range []string{`age>=18`, `age>=65`} {
		program, err := e.Compile(ctx, source)
		if err != nil {
			panic(err)
		}
		filter, err := local.Build(ctx, program)
		if err != nil {
			panic(err)
		}
		matched, err := filter.MatchInput(ctx, input)
		if err != nil {
			panic(err)
		}
		fmt.Println(source, matched)
	}

}
Complete runnable example
package main

import (
	"context"
	"fmt"

	"github.com/ornew/hff"
	"github.com/ornew/hff/backends/local"
	"github.com/ornew/hff/capabilities"
	"github.com/ornew/hff/engine"
	"github.com/ornew/hff/schema"
)

func main() {
	ctx := context.Background()
	e, err := engine.New(engine.WithSchema(schema.Struct(schema.Int("age"))),
		engine.WithCapabilities(capabilities.Standard()))
	if err != nil {
		panic(err)
	}
	record := hff.Object(map[string]hff.Value{"age": hff.Int(25)})
	input, err := e.Validate(ctx, record)
	if err != nil {
		panic(err)
	}
	// Validate the record once, then reuse it with programs of this exact schema.
	for _, source := range []string{`age>=18`, `age>=65`} {
		program, err := e.Compile(ctx, source)
		if err != nil {
			panic(err)
		}
		filter, err := local.Build(ctx, program)
		if err != nil {
			panic(err)
		}
		matched, err := filter.MatchInput(ctx, input)
		if err != nil {
			panic(err)
		}
		fmt.Println(source, matched)
	}
}

Output:

age>=18 true
age>=65 false

func (*Filter) Profile

Source

func (f *Filter) Profile() hff.Profile

Profile reports the built filter's actual budgets, time-zone snapshot, and registered extension declarations. A nil filter returns an invalid profile.

func (*Filter) TimeZoneVersion

Source

func (f *Filter) TimeZoneVersion() string

TimeZoneVersion reports the version supplied to Build. Empty means only the built-in UTC and fixed-offset zones are available.

type MethodFunc

Source

type MethodFunc func(call *Call, receiver hff.Value, args []hff.Value) (hff.Value, error)

MethodFunc implements one declared local extension. Implementations must be deterministic, side-effect-free, safe for concurrent calls, and cooperate with Context and budget charging. Return hff.Null(), nil for an ordinary data failure. ChargeSteps and ChargeGeneratedValues for variable work or output beyond the declaration's BaseCost; ignored charge errors still abort the call.

type MethodImplementation

Source

type MethodImplementation struct {
	// contains filtered or unexported fields
}

MethodImplementation pairs an immutable declaration with its local function. Use Implement to construct one and pass it with WithMethods to Build.

func Implement

Source

func Implement(declaration hff.MethodDeclaration, function MethodFunc) MethodImplementation

Implement pairs a declaration with the local implementation for that exact contract. Build rejects missing, duplicate, or contract-mismatched methods.

Example

{
	declaration, err := hff.NewMethodDeclaration(hff.MethodConfig{
		ID:	"example.emphasis", Name: "emphasis", Receiver: schema.StringType,
		Result:	schema.StringType, BaseCost: 1,
	})
	if err != nil {
		panic(err)
	}
	e, err := engine.New(
		engine.WithSchema(schema.Struct(schema.String("name"))),
		engine.WithCapabilities(capabilities.Standard().Allow(capabilities.Method(declaration.ID()))),
		engine.WithMethods(declaration),
	)
	if err != nil {
		panic(err)
	}
	ctx := context.Background()
	program, err := e.Compile(ctx, `name:emphasis="Alice!"`)
	if err != nil {
		panic(err)
	}
	implementation := local.Implement(declaration,
		func(call *local.Call, receiver hff.Value, args []hff.Value) (hff.Value, error) {
			text, _ := receiver.AsString()

			if err := call.ChargeSteps(len(text)); err != nil {
				return hff.Value{}, err
			}
			if err := call.ChargeGeneratedValues(1); err != nil {
				return hff.Value{}, err
			}
			return hff.String(text + "!"), nil
		})
	filter, err := local.Build(ctx, program, local.WithMethods(implementation))
	if err != nil {
		panic(err)
	}
	matched, err := filter.Match(ctx, hff.Object(map[string]hff.Value{"name": hff.String("Alice")}))
	if err != nil {
		panic(err)
	}
	fmt.Println(matched)

}
Complete runnable example
package main

import (
	"context"
	"fmt"

	"github.com/ornew/hff"
	"github.com/ornew/hff/backends/local"
	"github.com/ornew/hff/capabilities"
	"github.com/ornew/hff/engine"
	"github.com/ornew/hff/schema"
)

func main() {
	declaration, err := hff.NewMethodDeclaration(hff.MethodConfig{
		ID:	"example.emphasis", Name: "emphasis", Receiver: schema.StringType,
		Result:	schema.StringType, BaseCost: 1,
	})
	if err != nil {
		panic(err)
	}
	e, err := engine.New(
		engine.WithSchema(schema.Struct(schema.String("name"))),
		engine.WithCapabilities(capabilities.Standard().Allow(capabilities.Method(declaration.ID()))),
		engine.WithMethods(declaration),
	)
	if err != nil {
		panic(err)
	}
	ctx := context.Background()
	program, err := e.Compile(ctx, `name:emphasis="Alice!"`)
	if err != nil {
		panic(err)
	}
	implementation := local.Implement(declaration,
		func(call *local.Call, receiver hff.Value, args []hff.Value) (hff.Value, error) {
			text, _ := receiver.AsString()	// Dispatch has checked the declared type.
			// Account for work proportional to the input and the produced value.
			if err := call.ChargeSteps(len(text)); err != nil {
				return hff.Value{}, err
			}
			if err := call.ChargeGeneratedValues(1); err != nil {
				return hff.Value{}, err
			}
			return hff.String(text + "!"), nil
		})
	filter, err := local.Build(ctx, program, local.WithMethods(implementation))
	if err != nil {
		panic(err)
	}
	matched, err := filter.Match(ctx, hff.Object(map[string]hff.Value{"name": hff.String("Alice")}))
	if err != nil {
		panic(err)
	}
	fmt.Println(matched)
}

Output:

true

type Option

Source

type Option struct {
	// contains filtered or unexported fields
}

Option changes one local Build setting. Options apply left to right.

func WithConfig

Source

func WithConfig(config *Config) Option

WithConfig copies the supplied configuration into the Build options. A nil configuration is rejected; later options override its fields.

func WithLimits

Source

func WithLimits(l hff.Limits) Option

WithLimits replaces the complete local validation/evaluation Limits value and clears the unlimited setting. Zero fields use DefaultLimits, not the corresponding Program fields. To change one ceiling while preserving the Program's other budgets, copy its limits first:

limits := program.Limits()
limits.MaxSteps = 10000
filter, err := local.Build(ctx, program, local.WithLimits(limits))

This does not change the Program's compilation limits.

func WithMethods

Source

func WithMethods(methods ...MethodImplementation) Option

WithMethods replaces implementations configured for local Build. The slice is copied; the functions themselves must be safe for concurrent calls.

func WithTimeZones

Source

func WithTimeZones(profile *TimeZoneProfile) Option

WithTimeZones supplies a versioned named-zone snapshot for local Build. UTC and fixed offsets are built in; a nil profile is a configuration error.

func WithUnlimitedLimits

Source

func WithUnlimitedLimits() Option

WithUnlimitedLimits explicitly disables local validation and evaluation limits. Compilation limits remain those captured in the Program.

type TimeZoneProfile

Source

type TimeZoneProfile struct {
	// Version is a nonempty caller-supplied identifier for this TZif snapshot.
	Version	string
	// Zones maps accepted zone names to their TZif file contents.
	Zones	map[string][]byte
	// Aliases maps additional names directly to a zone in Zones or to UTC.
	Aliases	map[string]string
}

TimeZoneProfile supplies explicitly versioned TZif data for named zones. Version identifies the caller's data snapshot; HFF does not infer a version. Zones maps IANA-style names to TZif bytes. Aliases point directly to a Zones key or UTC and cannot form chains. Build copies and parses the profile; matching never loads host operating-system zone data.

Explore HFF
Search documentation

Search the language specification and guides.