opts

package module
v0.7.2 Latest Latest
Warning

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

Go to latest
Published: Jun 2, 2026 License: MIT Imports: 16 Imported by: 8

README

go-options

Lightweight helpers for wrapping application specific option structs with defaulting, validation, dynamic access helpers, and expression based rule evaluation. The package provides an Evaluator seam with adapters for expr-lang/expr, CEL-go, and goja (JavaScript) plus dynamic access helpers (Get, Set, Schema) for map or struct backed snapshots.

Installation

go get github.com/goliatone/go-options

Quick Start

package main

import (
	"fmt"

	opts "github.com/goliatone/go-options"
)

type NotificationOptions struct {
	Enabled bool
}

func main() {
	// Apply defaults.
	defaults := NotificationOptions{Enabled: true}
	current := opts.ApplyDefaults(NotificationOptions{}, defaults)

	// Wrap the snapshot and evaluate rules with the default expr adapter.
	wrapper := opts.New(
		map[string]any{
			"notifications": map[string]any{"enabled": current.Enabled},
		},
	)

	resp, err := wrapper.Evaluate("notifications.enabled")
	if err != nil {
		panic(err)
	}
fmt.Println("notifications.enabled:", resp.Value)
}

Scope Quick Start

The new scope primitives make it trivial to build multi-tenant stacks, inspect provenance, and surface scope metadata in schemas. See examples/scope/main.go for a runnable version of the snippets below.

Build a deterministic stack
type Settings struct {
	Notifications map[string]any
}

defaults := opts.NewLayer(
	opts.NewScope("defaults", 0, opts.WithScopeLabel("System Defaults")),
	Settings{Notifications: map[string]any{"email": map[string]any{"enabled": false}}},
)
tenant := opts.NewLayer(
	opts.NewScope("tenant", 50, opts.WithScopeMetadata(map[string]any{"slug": "acme"})),
	Settings{Notifications: map[string]any{"email": map[string]any{"enabled": true}}},
	opts.WithSnapshotID[Settings]("tenant/acme"),
)
user := opts.NewLayer(
	opts.NewScope("user", 100, opts.WithScopeLabel("User Override")),
	Settings{Notifications: map[string]any{"email": map[string]any{"enabled": true}}},
	opts.WithSnapshotID[Settings]("user/42"),
)

stack, err := opts.NewStack(defaults, tenant, user)
if err != nil {
	log.Fatalf("stack: %v", err)
}
options, err := stack.Merge(opts.WithScopeSchema(true))
if err != nil {
	log.Fatalf("merge: %v", err)
}
Resolve with trace
value, trace, err := options.ResolveWithTrace("Notifications.Email.Enabled")
if err != nil {
	log.Fatalf("trace: %v", err)
}
fmt.Printf("Effective value: %v\n", value)
for _, layer := range trace.Layers {
	fmt.Printf("%s (priority=%d) found=%v snapshot=%s\n",
		layer.Scope.Name, layer.Scope.Priority, layer.Found, layer.SnapshotID)
}

ResolveWithTrace walks layers strongest → weakest so you can explain why a value resolved the way it did. trace.Layers mirrors the JSON payload returned by Trace.ToJSON().

Scope-aware schemas
doc, err := options.Schema()
if err != nil {
	log.Fatalf("schema: %v", err)
}
for _, scope := range doc.Scopes {
	fmt.Printf("scope=%s priority=%d snapshot=%s\n", scope.Name, scope.Priority, scope.SnapshotID)
}

Because the stack was merged with opts.WithScopeSchema(true), each schema now emits an ordered list of scopes alongside the regular descriptor/OpenAPI payload.

State Storage (opt-in)

pkg/state provides persistence-facing contracts (load/save one scoped snapshot) plus a small resolver that hydrates an opts.Stack[T] and preserves provenance.

  • state.Store[T] loads/saves one state.Ref (domain + opts.Scope).
  • state.Resolver[T] loads scopes, builds opts.Layer[T] with SnapshotID, and merges with scope metadata enabled.
  • state.Ref.Identifier() builds canonical storage keys (system/tenant/org/team/user).

Try the runnable demo in examples/state:

go run ./examples/state

Defaults & Validation

ApplyDefaults returns the fallback struct when the current value is the zero value. Load combines construction with a validation pass. If the wrapped struct (or its pointer form) implements Validate() error, the hook fires automatically.

type ChannelOptions struct {
	Enabled bool
}

func (c ChannelOptions) Validate() error {
	if !c.Enabled {
		return errors.New("channel disabled")
	}
	return nil
}

wrapper, err := opts.Load(ChannelOptions{Enabled: false})
// err => "channel disabled"

Rule Evaluation

Expressions run against the snapshot stored in Options[T]. Evaluate(expr) uses the wrapped value as the environment. EvaluateWith(ctx, expr) lets callers override the snapshot and timestamp for a single evaluation. Every evaluator exposes the helper call("functionName", args...) which routes through the shared function registry so custom helpers behave consistently across engines.

registry := opts.NewFunctionRegistry()
_ = registry.Register("equalsIgnoreCase", func(args ...any) (any, error) {
	return strings.EqualFold(args[0].(string), args[1].(string)), nil
})

ctx := opts.RuleContext{
	Snapshot: map[string]any{
		"features": map[string]any{"newUI": true},
	},
	Metadata: map[string]any{
		"expected": "ENABLED",
		"actual":   "enabled",
	},
	Scope: opts.NewScope("tenant", opts.ScopePriorityTenant),
}

wrapper := opts.New(
	map[string]any{
		"features": map[string]any{"newUI": false},
	},
	opts.WithFunctionRegistry(registry),
	opts.WithScope(opts.NewScope("user:42", opts.ScopePriorityUser)),
)

resp, _ := wrapper.EvaluateWith(ctx, `scope.name == "tenant" && call("equalsIgnoreCase", metadata.expected, metadata.actual)`)
// resp.Value == true

Rule Context

RuleContext carries:

  • Snapshot any – evaluation target (defaults to the wrapped value).
  • Now *time.Time – defaults to time.Now() when omitted.
  • Args map[string]any – ad hoc inputs you want available to expressions.
  • Metadata map[string]any – auxiliary information (for logging, audit, etc.).
  • Scope opts.Scope – structured metadata describing the active layer; evaluators expose it to expressions as scope.
  • ScopeName string – legacy string label retained for callers that only know the raw scope identifier.

Use opts.WithScope(...) when constructing an options wrapper to seed this metadata automatically; it will be copied into every RuleContext unless you override it per invocation.

Activity (opt-in)

go-options can emit activity events using the shared hook pattern from go-cms/go-notifications. Everything is opt-in: no hooks configured means no emissions.

  • Add hooks when constructing options: opts.New(value, opts.WithActivityHooks(activity.Hooks{&activity.CaptureHook{}})) or include usersink.Hook{Sink: yourActivitySink} to forward into go-users.
  • Build lifecycle events with helpers like activity.BuildOptionsUpdatedEvent(...) (path, scope metadata, old/new values) and fan them out via opts.ActivityHooks().Notify(ctx, evt).
  • Try the runnable demo in examples/activity to see capture + usersink hooks in action.
  • See docs/ACTIVITY.md for contracts, wiring, and adapter details.

All fields default to zero cost empty values so existing call sites continue to work unchanged.

Dynamic Paths & Schema

The wrapper offers dynamic helpers while keeping direct struct access the primary path:

import (
	openapi "github.com/goliatone/go-options/schema/openapi"
)

wrapper := opts.New(map[string]any{
	"channels": map[string]any{
		"email": map[string]any{"enabled": true},
	},
})

enabled, _ := wrapper.Get("channels.email.enabled")
// enabled == true

_ = wrapper.Set("channels.push.enabled", true) // lazily creates intermediate maps

doc := wrapper.MustSchema()
fields, ok := doc.Document.([]opts.FieldDescriptor)
if !ok {
	log.Fatalf("unexpected schema payload %T", doc.Document)
}
for _, field := range fields {
	fmt.Printf("%s => %s\n", field.Path, field.Type)
}

openAPIDoc, err := wrapper.WithSchemaGenerator(openapi.NewGenerator()).Schema()
if err != nil {
	log.Fatalf("openapi schema: %v", err)
}
fmt.Println(openAPIDoc.Format) // "openapi"

Key details:

  • Get traverses maps with string keys, exported struct fields, or fields tagged with json:"name". It also supports slice/array indices (items.0.id).
  • Set mutates map backed snapshots and lazily creates intermediate maps. Struct backed values are read only; attempting to call Set with a struct snapshot returns an error.
  • Schema() returns a SchemaDocument describing the wrapped value. The default generator emits flattened FieldDescriptor paths. Pass opts.WithSchemaGenerator(...) (or schema/openapi.Option()) to swap in alternate representations such as OpenAPI/JSON Schema.
  • Opt into scope descriptors by merging stacks with opts.WithScopeSchema(true); SchemaDocument.Scopes then lists every layer (name, label, priority, snapshot ID, metadata) alongside the generated schema.
Schema Generators

Options.Schema() delegates to a configurable SchemaGenerator. The default generator produces a slice of FieldDescriptor values (format descriptors). To generate OpenAPI compatible schemas:

import openapi "github.com/goliatone/go-options/schema/openapi"

wrapper := opts.New(snapshot, openapi.Option(
	openapi.WithInfo("Config Service", "1.2.0"),
	openapi.WithOperation("/config", "POST", "createConfig"),
	openapi.WithResponse("204", "Configuration accepted"),
))
doc, _ := wrapper.Schema()
if doc.Format == opts.SchemaFormatOpenAPI {
	fmt.Printf("properties: %#v\n", doc.Document)
}

The OpenAPI generator is configurable through functional options:

  • openapi.WithOpenAPIVersion("3.1.0")
  • openapi.WithInfo("Service Title", "version", openapi.WithInfoDescription("..."))
  • openapi.WithOperation("/path", "METHOD", "operationId", openapi.WithOperationSummary("..."))
  • openapi.WithContentType("application/json")
  • openapi.WithResponse("204", "Description")

See docs/SCHEMA_TDD.md for the design background and future roadmap for schema exports.

Custom generators implement:

type SchemaGenerator interface {
    Generate(value any) (opts.SchemaDocument, error)
}

Generators must be safe for concurrent use and handle nil inputs by returning an empty schema (usually { "type": "null" }). Use opts.WithSchemaGenerator(myGenerator) or opts.Options.WithSchemaGenerator(...) to attach custom implementations per wrapper.

Custom Functions & Shared Registry

Use opts.WithCustomFunction(name, fn) or opts.WithFunctionRegistry(registry) when constructing the wrapper. Functions accept a variadic []any payload and may return (any, error); returning an error propagates to the evaluator. Expressions call helpers via call("functionName", args...). The expr adapter additionally exposes direct symbols for convenience.

registry := opts.NewFunctionRegistry()
_ = registry.Register("withinQuietHours", func(args ...any) (any, error) {
	now := args[0].(time.Time)
	start := args[1].(time.Time)
	end := args[2].(time.Time)
	return (now.Equal(start) || now.After(start)) && now.Before(end), nil
})

wrapper := opts.New(snapshot,
	opts.WithFunctionRegistry(registry),
)

resp, err := wrapper.EvaluateWith(
	opts.RuleContext{Snapshot: snapshot, Now: &now},
	`call("withinQuietHours", now, QuietHours.Start, QuietHours.End)`,
)

Evaluator Options & Caching

All evaluators share the same configuration surface:

  • opts.NewExprEvaluator(...) – expr-lang/expr wrapper.
  • opts.NewCELEvaluator(...) – CEL-go adapter.
  • opts.NewJSEvaluator(...) – goja/JavaScript adapter (requires js_eval build tag).
  • opts.WithEvaluator – plug the evaluator into the wrapper.
  • opts.WithProgramCache(cache) – supply a memoisation layer for compiled programs (used by expr, CEL, and JS adapters).
  • ExprWithProgramCache, CELWithProgramCache, and JSWithProgramCache wire caches directly when you build adapters manually.
  • ExprWithFunctionRegistry, CELWithFunctionRegistry, and JSWithFunctionRegistry keep custom functions in sync.

When no evaluator is configured, opts.New defaults to the expr adapter automatically.

Layering

LayerWith merges snapshots ordered strongest to weakest with the current snapshot as the fallback, returning a new wrapper with the merged value.

defaults := AppOptions{Timeout: 30, Retries: 3}
groupDefaults := AppOptions{Timeout: 60}
userSettings := AppOptions{Retries: 5}

wrapper := opts.New(defaults)
merged := wrapper.LayerWith(userSettings, groupDefaults)
// merged.Value => AppOptions{Timeout: 60, Retries: 5}

// Canonical five-layer stack helper:
stacked, _ := opts.SystemTenantOrgTeamUser(defaults, tenant, org, team, userSettings)

Scope Stacks & Tracing

Construct deterministic stacks with named scopes, merge them, then inspect provenance:

stack, _ := opts.NewStack(
	opts.NewLayer(opts.NewScope("defaults", 0), DefaultSettings),
	opts.NewLayer(opts.NewScope("tenant", 50), TenantSettings, opts.WithSnapshotID[Settings]("tenant/acme")),
	opts.NewLayer(opts.NewScope("user", 100), UserSettings, opts.WithSnapshotID[Settings]("user/42")),
)
options, _ := stack.Merge()

value, trace, _ := options.ResolveWithTrace("Notifications.Email.Enabled")
fmt.Println(value)           // strongest layer's value
_ = json.NewEncoder(os.Stdout).Encode(trace)

provenance, _ := options.FlattenWithProvenance()
for _, entry := range provenance {
	fmt.Printf("%s => scope=%s snapshot=%s\n", entry.Path, entry.Scope.Name, entry.SnapshotID)
}

ResolveWithTrace walks every layer from strongest → weakest so you always know which scope supplied a value (or why it fell back). FlattenWithProvenance enumerates all reachable paths for documentation/debug tooling.

Evaluator Logging

Attach a logger to track evaluation events:

logger := opts.EvaluatorLoggerFunc(func(event opts.EvaluatorLogEvent) {
	log.Printf("[%s] expr=%q scope=%q duration=%v err=%v",
		event.Engine, event.Expr, event.Scope, event.Duration, event.Err)
})

wrapper := opts.New(snapshot, opts.WithEvaluatorLogger(logger))

Documentation

Index

Constants

View Source
const (
	// Recommended priorities for common layering patterns. Higher numbers win.
	ScopePrioritySystem = 100
	ScopePriorityTenant = 200
	ScopePriorityOrg    = 300
	ScopePriorityTeam   = 400
	ScopePriorityUser   = 500
)

Variables

View Source
var (
	// ErrScopeNameRequired indicates a missing scope name.
	ErrScopeNameRequired = errors.New("scope: name must be provided")
	// ErrDuplicateScopeName indicates Stack construction received multiple
	// layers with the same scope name.
	ErrDuplicateScopeName = errors.New("scope: names must be unique")
	// ErrPriorityOrder indicates Stack construction detected duplicate or
	// unsorted priorities.
	ErrPriorityOrder = errors.New("scope: priorities must be strictly ordered")
)
View Source
var ErrNoEvaluator = errors.New("opts: evaluator not configured")

Functions

func ApplyDefaults

func ApplyDefaults[T any](value T, defaults T) T

ApplyDefaults returns value if it is already populated, otherwise it falls back to defaults.

Types

type CELEvaluatorOption

type CELEvaluatorOption func(*celEvaluator)

CELEvaluatorOption configures the CEL evaluator.

func CELWithFunctionRegistry

func CELWithFunctionRegistry(registry *FunctionRegistry) CELEvaluatorOption

CELWithFunctionRegistry wires a FunctionRegistry into the CEL evaluator.

func CELWithProgramCache

func CELWithProgramCache(cache ProgramCache) CELEvaluatorOption

CELWithProgramCache wires a ProgramCache into the CEL evaluator.

type CompileOption

type CompileOption interface {
	// contains filtered or unexported methods
}

CompileOption configures evaluator compile behaviour.

type CompiledRule

type CompiledRule interface {
	Evaluate(ctx RuleContext) (any, error)
}

CompiledRule represents a reusable expression program.

type EvaluationError

type EvaluationError struct {
	Engine string
	Expr   string
	Scope  string
	Err    error
}

EvaluationError captures evaluator metadata alongside the originating error.

func (*EvaluationError) Error

func (e *EvaluationError) Error() string

func (*EvaluationError) Unwrap

func (e *EvaluationError) Unwrap() error

type Evaluator

type Evaluator interface {
	Evaluate(ctx RuleContext, expr string) (any, error)
	Compile(expr string, opts ...CompileOption) (CompiledRule, error)
}

Evaluator executes expressions against a rule context.

func NewCELEvaluator

func NewCELEvaluator(opts ...CELEvaluatorOption) Evaluator

NewCELEvaluator constructs an Evaluator backed by cel-go.

func NewExprEvaluator

func NewExprEvaluator(opts ...ExprEvaluatorOption) Evaluator

NewExprEvaluator constructs an Evaluator backed by expr-lang/expr.

func NewJSEvaluator

func NewJSEvaluator(opts ...JSEvaluatorOption) Evaluator

NewJSEvaluator is unavailable without the js_eval build tag.

type EvaluatorLogEvent

type EvaluatorLogEvent struct {
	Engine   string
	Expr     string
	Scope    string
	Duration time.Duration
	Err      error
}

EvaluatorLogEvent describes an evaluation attempt for logging.

type EvaluatorLogger

type EvaluatorLogger interface {
	LogEvaluation(EvaluatorLogEvent)
}

EvaluatorLogger records evaluator events.

type EvaluatorLoggerFunc

type EvaluatorLoggerFunc func(EvaluatorLogEvent)

EvaluatorLoggerFunc adapts a function to EvaluatorLogger.

func (EvaluatorLoggerFunc) LogEvaluation

func (f EvaluatorLoggerFunc) LogEvaluation(event EvaluatorLogEvent)

LogEvaluation implements EvaluatorLogger.

type ExprEvaluatorOption

type ExprEvaluatorOption func(*exprEvaluator)

ExprEvaluatorOption configures an expr evaluator instance.

func ExprWithFunctionRegistry

func ExprWithFunctionRegistry(registry *FunctionRegistry) ExprEvaluatorOption

ExprWithFunctionRegistry wires a FunctionRegistry into the expr evaluator.

func ExprWithProgramCache

func ExprWithProgramCache(cache ProgramCache) ExprEvaluatorOption

ExprWithProgramCache wires a ProgramCache into the expr evaluator.

type FieldDescriptor

type FieldDescriptor struct {
	Path string
	Type string
}

FieldDescriptor describes a path and the inferred type.

type Function

type Function func(args ...any) (any, error)

Function represents a callable registered against evaluators.

type FunctionEntry

type FunctionEntry struct {
	Name string
	Fn   Function
}

FunctionEntry represents a single registration to apply to a registry.

type FunctionRegistry

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

FunctionRegistry stores custom functions keyed by name.

func MustFunctionRegistry

func MustFunctionRegistry(entries ...FunctionEntry) *FunctionRegistry

MustFunctionRegistry constructs a registry from entries and panics on failure.

func NewFunctionRegistry

func NewFunctionRegistry() *FunctionRegistry

NewFunctionRegistry constructs an empty registry.

func NewFunctionRegistryFrom

func NewFunctionRegistryFrom(entries ...FunctionEntry) (*FunctionRegistry, error)

NewFunctionRegistryFrom constructs a registry seeded with entries.

func (*FunctionRegistry) Call

func (r *FunctionRegistry) Call(name string, args ...any) (any, error)

Call executes the function registered for name.

func (*FunctionRegistry) Clone

func (r *FunctionRegistry) Clone() *FunctionRegistry

Clone returns a shallow copy of the registry.

func (*FunctionRegistry) Names

func (r *FunctionRegistry) Names() []string

Names returns registered function names sorted alphabetically.

func (*FunctionRegistry) Register

func (r *FunctionRegistry) Register(name string, fn Function) error

Register stores fn under name guarding against duplicates.

type JSEvaluatorOption

type JSEvaluatorOption func(*jsEvaluatorConfig)

JSEvaluatorOption configures the JS evaluator.

func JSWithFunctionRegistry

func JSWithFunctionRegistry(registry *FunctionRegistry) JSEvaluatorOption

JSWithFunctionRegistry applies a FunctionRegistry to the JS evaluator.

func JSWithProgramCache

func JSWithProgramCache(cache ProgramCache) JSEvaluatorOption

JSWithProgramCache applies a ProgramCache to the JS evaluator.

type Layer

type Layer[T any] struct {
	Scope      Scope
	Snapshot   T
	SnapshotID string
}

Layer pairs a scope definition with the snapshot captured for that scope.

func NewLayer

func NewLayer[T any](scope Scope, snapshot T, opts ...LayerOption[T]) Layer[T]

NewLayer constructs a Layer with immutable copies of both the scope metadata and snapshot payload.

type LayerOption

type LayerOption[T any] func(*Layer[T])

LayerOption configures optional metadata for a layer.

func WithSnapshotID

func WithSnapshotID[T any](id string) LayerOption[T]

WithSnapshotID sets the snapshot identifier used for auditing.

type Option

type Option func(*optionsConfig)

func WithActivityHooks added in v0.5.0

func WithActivityHooks(hooks activity.Hooks) Option

WithActivityHooks attaches activity hooks to the Options configuration. Hooks are cloned and nil entries dropped to preserve immutability.

func WithCustomFunction

func WithCustomFunction(name string, fn Function) Option

WithCustomFunction registers fn under name for the wrapper.

func WithEvaluator

func WithEvaluator(e Evaluator) Option

WithEvaluator configures an evaluator on the Options wrapper.

func WithEvaluatorLogger

func WithEvaluatorLogger(logger EvaluatorLogger) Option

WithEvaluatorLogger attaches an evaluator logger to the Options wrapper.

func WithFunctionRegistry

func WithFunctionRegistry(registry *FunctionRegistry) Option

WithFunctionRegistry configures a wrapper to use registry.

func WithProgramCache

func WithProgramCache(cache ProgramCache) Option

WithProgramCache registers a program cache on the Options wrapper.

func WithSchemaGenerator

func WithSchemaGenerator(generator SchemaGenerator) Option

WithSchemaGenerator configures a custom schema generator implementation.

func WithScope

func WithScope(scope Scope) Option

WithScope configures the default scope metadata applied to evaluator contexts.

func WithScopeSchema

func WithScopeSchema(include bool) Option

WithScopeSchema toggles inclusion of scope metadata within generated schemas.

type Options

type Options[T any] struct {
	Value T
	// contains filtered or unexported fields
}

Options holds a typed options value and evaluator configuration.

func Load

func Load[T any](value T, opts ...Option) (*Options[T], error)

Load constructs an Options wrapper and runs validation when supported by the underlying type.

func New

func New[T any](value T, opts ...Option) *Options[T]

New constructs an Options wrapper around the provided value.

func SystemTenantOrgTeamUser

func SystemTenantOrgTeamUser[T any](system, tenant, org, team, user T) (*Options[T], error)

SystemTenantOrgTeamUser assembles a canonical five-layer stack (system → tenant → org → team → user) and returns the merged options wrapper.

func (*Options[T]) ActivityHooks added in v0.5.0

func (o *Options[T]) ActivityHooks() activity.Hooks

ActivityHooks returns a cloned slice of activity hooks configured on the options wrapper. The returned slice can be safely mutated by the caller.

func (*Options[T]) Clone

func (o *Options[T]) Clone() *Options[T]

Clone returns a shallow copy of the Options wrapper preserving configuration.

func (*Options[T]) Evaluate

func (o *Options[T]) Evaluate(expr string) (Response[any], error)

Evaluate executes expr using the configured evaluator and wraps the result.

func (*Options[T]) EvaluateWith

func (o *Options[T]) EvaluateWith(ctx RuleContext, expr string) (Response[any], error)

EvaluateWith executes expr using ctx, falling back to the wrapped value when ctx.Snapshot is nil.

func (*Options[T]) FlattenWithProvenance

func (o *Options[T]) FlattenWithProvenance() ([]Provenance, error)

FlattenWithProvenance enumerates every reachable path in the wrapped value and reports which scope supplied the effective value.

func (*Options[T]) Get

func (o *Options[T]) Get(path string) (any, error)

Get retrieves the value located at path using dot notation.

func (*Options[T]) LayerWith

func (o *Options[T]) LayerWith(layers ...T) *Options[T]

LayerWith merges layers ordered strongest to weakest with the current snapshot as the fallback, returning a new wrapper with the merged value.

func (*Options[T]) MustSchema

func (o *Options[T]) MustSchema() SchemaDocument

MustSchema generates a schema document and panics on error.

func (*Options[T]) ResolveWithTrace

func (o *Options[T]) ResolveWithTrace(path string) (any, Trace, error)

ResolveWithTrace returns the effective value for path along with provenance from each scope layer that was inspected.

func (*Options[T]) Schema

func (o *Options[T]) Schema() (SchemaDocument, error)

Schema returns a schema document for the wrapped value.

func (*Options[T]) Set

func (o *Options[T]) Set(path string, value any) error

Set stores value at the path creating intermediate maps as needed.

func (*Options[T]) Validate

func (o *Options[T]) Validate() error

Validate invokes the Validate method on the wrapped value when present.

func (*Options[T]) WithSchemaGenerator

func (o *Options[T]) WithSchemaGenerator(generator SchemaGenerator) *Options[T]

WithSchemaGenerator returns a cloned wrapper configured with generator. Passing a nil generator removes any custom generator and falls back to the default.

func (*Options[T]) WithValue

func (o *Options[T]) WithValue(value T) *Options[T]

WithValue returns a cloned wrapper whose Value field is set to value, leaving the original untouched.

type ProgramCache

type ProgramCache interface {
	Get(key string) (any, bool)
	Set(key string, value any)
}

ProgramCache stores compiled expression programs keyed by expression strings.

type Provenance

type Provenance struct {
	Scope      Scope  `json:"scope"`
	SnapshotID string `json:"snapshot_id,omitempty"`
	Path       string `json:"path"`
	Value      any    `json:"value,omitempty"`
	Found      bool   `json:"found"`
}

Provenance details how a specific scope contributed to a traced path.

type Response

type Response[T any] struct {
	Value T
}

Response stores a typed result produced by an evaluator.

type RuleContext

type RuleContext struct {
	Snapshot  any
	Now       *time.Time
	Args      map[string]any
	Metadata  map[string]any
	Scope     Scope
	ScopeName string
}

RuleContext carries inputs needed when evaluating an expression.

type SchemaDocument

type SchemaDocument struct {
	Format   SchemaFormat
	Document any
	Scopes   []SchemaScope
}

SchemaDocument encapsulates a generated schema output alongside its format identifier. Implementations must ensure Document is JSON-serialisable.

type SchemaFormat

type SchemaFormat string

SchemaFormat identifies the representation a schema document encodes.

const (
	// SchemaFormatDescriptors represents the flattened field descriptors.
	SchemaFormatDescriptors SchemaFormat = "descriptors"
	// SchemaFormatOpenAPI represents OpenAPI-compatible JSON Schema documents.
	SchemaFormatOpenAPI SchemaFormat = "openapi"
)

type SchemaGenerator

type SchemaGenerator interface {
	Generate(value any) (SchemaDocument, error)
}

SchemaGenerator transforms an options value into a schema document. All implementations MUST be safe for concurrent use and handle nil inputs by returning an empty schema document.

func DefaultSchemaGenerator

func DefaultSchemaGenerator() SchemaGenerator

DefaultSchemaGenerator returns the built-in descriptor-based schema generator.

type SchemaScope

type SchemaScope struct {
	Name       string         `json:"name"`
	Label      string         `json:"label,omitempty"`
	Priority   int            `json:"priority"`
	Metadata   map[string]any `json:"metadata,omitempty"`
	SnapshotID string         `json:"snapshot_id,omitempty"`
}

SchemaScope describes a single scope entry included in a schema document.

type Scope

type Scope struct {
	Name     string
	Label    string
	Priority int
	Metadata map[string]any
}

Scope models a named precedence bucket (system, tenant, user, etc.). Higher priority values represent stronger layers.

func NewScope

func NewScope(name string, priority int, opts ...ScopeOption) Scope

NewScope builds a Scope with the supplied configuration. Validation is deferred to Stack construction so callers can assemble scopes before deciding precedence.

type ScopeOption

type ScopeOption func(*scopeConfig)

ScopeOption configures metadata on Scope creation.

func WithScopeLabel

func WithScopeLabel(label string) ScopeOption

WithScopeLabel sets a human-friendly label on the scope.

func WithScopeMetadata

func WithScopeMetadata(metadata map[string]any) ScopeOption

WithScopeMetadata attaches arbitrary metadata to the scope. The map is copied so the resulting Scope remains immutable even if the caller mutates their reference.

type Stack

type Stack[T any] struct {
	// contains filtered or unexported fields
}

Stack represents an immutable, scope-aware layering configuration ordered from strongest to weakest precedence.

func NewStack

func NewStack[T any](layers ...Layer[T]) (*Stack[T], error)

NewStack validates and sorts the supplied layers so that the strongest scope (highest priority) is first. Layers and their snapshots are deep copied to guarantee read-only safety after construction.

func (*Stack[T]) Layers

func (s *Stack[T]) Layers() []Layer[T]

Layers returns a defensive copy of the underlying layers to preserve immutability guarantees.

func (*Stack[T]) Len

func (s *Stack[T]) Len() int

Len returns the number of layers in the stack.

func (*Stack[T]) Merge

func (s *Stack[T]) Merge(opts ...Option) (*Options[T], error)

Merge resolves the stack into an Options wrapper that retains provenance metadata for each contributing layer. The provided Option arguments apply to the resulting wrapper.

type Trace

type Trace struct {
	Path   string       `json:"path"`
	Layers []Provenance `json:"layers"`
}

Trace captures provenance information for a given path lookup across the scoped layers that produced the effective value.

func TraceFromJSON

func TraceFromJSON(payload []byte) (Trace, error)

TraceFromJSON deserialises a JSON payload that was previously generated via ToJSON.

func (Trace) ToJSON

func (t Trace) ToJSON() ([]byte, error)

ToJSON serialises the trace into JSON for logging or transport helpers.

Directories

Path Synopsis
examples
activity command
options command
scope command
state command
internal
pkg
state
Package state defines persistence-facing contracts for loading and saving per-scope option snapshots, plus a small resolver that orchestrates scope loading and delegates layering/provenance to the core go-options primitives.
Package state defines persistence-facing contracts for loading and saving per-scope option snapshots, plus a small resolver that orchestrates scope loading and delegates layering/provenance to the core go-options primitives.
schema

Jump to

Keyboard shortcuts

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