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
func Profile() hff.Profiletype Calltype Configtype Filterfunc Build(ctx context.Context, program hff.Program, options ...Option) (*Filter, error)func (f *Filter) Match(ctx context.Context, record hff.Value) (bool, error)func (f *Filter) MatchInput(ctx context.Context, input hff.Input) (bool, error)func (f *Filter) Profile() hff.Profilefunc (f *Filter) TimeZoneVersion() string
type MethodFunctype MethodImplementationtype Optiontype TimeZoneProfile
Functions
func Profile
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
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
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
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
func (c *Call) Context() context.Context
Context returns the context for this invocation, or nil for a nil Call.
type Config
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
type Filter struct {
// contains filtered or unexported fields
}
Filter snapshots its configuration. Each call owns independent evaluation state.
func Build
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
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
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
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
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
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
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
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
type Option struct {
// contains filtered or unexported fields
}
Option changes one local Build setting. Options apply left to right.
func WithConfig
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
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
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
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
func WithUnlimitedLimits() Option
WithUnlimitedLimits explicitly disables local validation and evaluation limits. Compilation limits remain those captured in the Program.
type TimeZoneProfile
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.