Documentation
¶
Index ¶
- Constants
- Variables
- func AnyOf(vals ...string) anyOf
- func Load(r io.Reader) (*Engine, []Warning, error)
- func LoadFileWithOptions(path string, opts ...Option) (*Engine, []Warning, error)
- func LoadWithOptions(r io.Reader, opts ...Option) (*Engine, []Warning, error)
- func New(opts ...Option) (*Engine, []Warning, error)
- func NewFromConfig(cfg Config, opts ...Option) (*Engine, []Warning, error)
- type Action
- type ClosestResult
- type Config
- type Dimension
- type DimensionConfig
- type Engine
- func (e *Engine) Check(values ...string) (bool, error)
- func (e *Engine) Closest(values ...string) (*ClosestResult, error)
- func (e *Engine) ClosestIn(dim any, values ...string) (*ClosestResult, error)
- func (e *Engine) Dimensions() []Dimension
- func (e *Engine) Explain(values ...string) (Explanation, error)
- func (e *Engine) PartialCheck(values ...string) (bool, error)
- func (e *Engine) Rules() []Rule
- type Explanation
- type Option
- type Rule
- type RuleConfig
- type TiebreakStrategy
- type Warning
- type WarningKind
- type WildcardType
Constants ¶
const DefaultAnalysisLimit = 100_000
DefaultAnalysisLimit is the default upper bound on the number of dimension tuples enumerated for shadowed-rule analysis in New. Dead-rule detection does not use this cap. Use WithAnalysisLimit to change the threshold.
Variables ¶
var ( // ErrArityMismatch is returned by [Engine.Check] when the number of // arguments does not equal the number of dimensions. ErrArityMismatch = errors.New("gorege: argument count must match dimension count") // ErrRuleTooWide is returned by [New] when a rule has more matchers than dimensions. ErrRuleTooWide = errors.New("gorege: rule has more matchers than dimensions") // ErrUnknownDimensionValue is returned by [New] when a matcher references a // value not present in the corresponding dimension declaration. ErrUnknownDimensionValue = errors.New("gorege: matcher references unknown dimension value") // ErrInvalidDimension is returned by [Engine.ClosestIn] when dim is not a // valid index (int-sized signed/unsigned types) or a known dimension name. ErrInvalidDimension = errors.New("gorege: invalid dimension selector") // ErrUnsupportedConfigFormat is returned by [LoadFileWithOptions] when the // path does not end in .json. ErrUnsupportedConfigFormat = errors.New("gorege: unsupported config format (use .json)") )
Functions ¶
func Load ¶
Load decodes JSON from r into an engine. Equivalent to LoadWithOptions with no extra options.
func LoadFileWithOptions ¶
LoadFileWithOptions reads a JSON engine definition from path. The file extension must be .json. It decodes the file and calls LoadWithOptions, appending opts after the JSON-derived WithDimensions and WithRules. Pass no opts for the same behaviour as LoadWithOptions on the file bytes. Use opts to set WithAnalysisLimit, WithTiebreak, or other Option values when loading large configs.
Hot reload: build a new engine with LoadFileWithOptions and swap a sync/atomic.Pointer value holding the active *Engine so readers always load through that pointer.
func LoadWithOptions ¶
LoadWithOptions decodes JSON from r into a Config and calls NewFromConfig. Later options override earlier ones for the same setting (e.g. a second WithDimensions replaces dimensions from JSON).
func New ¶
New builds an immutable engine. It validates matchers against dimensions and returns warnings for dead or shadowed rules.
Dead rules are detected without enumerating the Cartesian product. Shadowed rules are detected by walking that product; for large dimension sets this can be expensive. The default upper bound is DefaultAnalysisLimit tuples for shadow analysis only. Use WithAnalysisLimit to raise, lower, or disable (negative value) analysis. When the product exceeds the limit, a Warning with kind WarningKindAnalysisLimitExceeded is returned and shadow analysis is skipped; dead detection still runs.
func NewFromConfig ¶
NewFromConfig builds an engine from a populated Config. It runs the same validation and analysis as Load / LoadFileWithOptions. Callers are responsible for parsing.
opts are applied after WithDimensions and WithRules derived from the config, so options such as WithAnalysisLimit and WithTiebreak can override those settings.
Types ¶
type ClosestResult ¶
type ClosestResult struct {
Conditions []string
// Distance is the Hamming distance from the input tuple to Conditions (number
// of dimensions whose value differs).
Distance int
DimIndex int
DimName string
Value string
}
ClosestResult is returned by Engine.Closest and Engine.ClosestIn when an allowed tuple exists. DimIndex names the primary dimension reported for that hit (see tiebreak strategy). If no allowed combination exists, both methods return a nil pointer.
type Config ¶
type Config struct {
Dimensions []DimensionConfig `json:"dimensions" yaml:"dimensions"`
Rules []RuleConfig `json:"rules" yaml:"rules"`
}
Config holds the raw configuration needed to build a gorege engine. Callers are responsible for parsing; this struct only carries data into the engine.
To use YAML or TOML, import a parser in your own project and populate this struct, for example:
var cfg gorege.Config
if err := yaml.Unmarshal(data, &cfg); err != nil { ... }
e, _, err := gorege.NewFromConfig(cfg)
The gorege module itself uses only encoding/json.
type Dimension ¶
type Dimension struct {
// contains filtered or unexported fields
}
Dimension describes one axis of the decision tuple. Values are the allowed strings for that axis when dimensions are declared.
func Dim ¶
Dim creates a named dimension. The name enables ClosestIn(name, ...) and clearer warnings once those features are wired up.
type DimensionConfig ¶
type DimensionConfig struct {
Name string `json:"name" yaml:"name"`
Values []string `json:"values" yaml:"values"`
}
DimensionConfig describes one dimension axis. If Name is empty, the dimension is anonymous (DimValues semantics).
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine evaluates a frozen rule set. It is safe for concurrent use.
func (*Engine) Check ¶
Check evaluates the input tuple with strict arity: len(values) must equal the number of dimensions. First matching rule wins; if none match, false.
func (*Engine) Closest ¶
func (e *Engine) Closest(values ...string) (*ClosestResult, error)
Closest searches for a nearest allowed tuple using breadth-first Hamming distance: distance 1, then 2, … until a candidate passes Engine.Check. Tiebreak controls subset and reporting order; the returned ClosestResult highlights one primary changed dimension consistent with that strategy.
func (*Engine) ClosestIn ¶
func (e *Engine) ClosestIn(dim any, values ...string) (*ClosestResult, error)
ClosestIn restricts the search to a single dimension. dim may be a dimension index (int, int32, int64, uint, uint32, uint64) or a non-empty dimension name (string). Returns nil when no value in that dimension yields an allowed tuple.
func (*Engine) Dimensions ¶
Dimensions returns the engine dimensions in order (defensive copy).
func (*Engine) Explain ¶
func (e *Engine) Explain(values ...string) (Explanation, error)
Explain returns which rule matched first, if any. Arity follows Engine.Check. When no rule matches, Matched is false, Allowed is false, and RuleIndex is -1.
func (*Engine) PartialCheck ¶
PartialCheck allows a shorter input prefix (including an empty prefix). Trailing dimensions are unconstrained: a matcher at those positions is treated as satisfied for ALLOW rules and as failed for DENY rules (Recht-style behaviour). The empty prefix means “no values fixed yet”: it is not an arity error (unlike Engine.Check, which requires a full tuple). Semantically it answers whether any completion could still be allowed—for example, after PartialCheck("Guest") asks whether Guest can access for some day, PartialCheck() asks whether anyone can access for some full tuple.
If len(values) is greater than the number of dimensions, it returns ErrArityMismatch so misuse is not conflated with an implicit deny (false, nil).
func (*Engine) Rules ¶
Rules returns the rules in first-match order (defensive copy). Matchers are not exported; use [Rule.Name] and Rule.Action for inspection, or rebuild logic via Engine.Check / Engine.Explain.
type Explanation ¶
Explanation is the outcome of Engine.Explain for a full input tuple.
type Option ¶
type Option func(*engineConfig) error
Option configures New.
func WithAnalysisLimit ¶
WithAnalysisLimit sets the upper bound on tuples scanned for shadowed-rule analysis (Cartesian enumeration) in New. Dead-rule detection does not use this cap.
- n == 0: use DefaultAnalysisLimit.
- n < 0: skip analysis entirely (no warnings).
- n > 0: if the dimension value product exceeds n, shadow analysis is skipped and New returns a Warning with kind WarningKindAnalysisLimitExceeded.
func WithDimensions ¶
WithDimensions sets the ordered dimension tuple. May be empty.
func WithTiebreak ¶
func WithTiebreak(s TiebreakStrategy) Option
WithTiebreak sets the TiebreakStrategy used by Engine.Closest. The zero value selects TiebreakLeftmostDim.
type Rule ¶
type Rule struct {
Name string
// contains filtered or unexported fields
}
Rule is a single first-match rule. Use Allow / Deny constructors.
func Allow ¶
Allow builds an ALLOW rule. Each part may be:
- string — exact match
- WildcardType — match any declared value in that dimension (or any string if the engine has no dimensions)
- anyOf — match any of the listed values (from AnyOf)
type RuleConfig ¶
type RuleConfig struct {
Action string `json:"action" yaml:"action"`
Name string `json:"name" yaml:"name"`
Conditions []any `json:"conditions" yaml:"conditions"`
}
RuleConfig describes a single rule. Each element of Conditions may be:
- string — exact match or "*" wildcard
- []any — AnyOf list (as produced by encoding/json)
- []string — AnyOf list (as some YAML decoders produce)
type TiebreakStrategy ¶
type TiebreakStrategy int
TiebreakStrategy orders equally-distant candidates in Engine.Closest.
const ( // TiebreakLeftmostDim prefers the smallest index among changed dimensions // when reporting [ClosestResult], and tries subset combinations in // increasing lexicographic index order. TiebreakLeftmostDim TiebreakStrategy = iota // TiebreakRightmostDim prefers the largest changed dimension index and tries // subsets with larger indices first. TiebreakRightmostDim // TiebreakDeclOrder matches [TiebreakLeftmostDim] for this implementation // (dimensions are already in declaration order). TiebreakDeclOrder )
func (TiebreakStrategy) GoString ¶
func (t TiebreakStrategy) GoString() string
GoString satisfies fmt.GoStringer for TiebreakStrategy.
type Warning ¶
type Warning struct {
Kind WarningKind
Message string
}
Warning describes a non-fatal issue detected at engine construction time.
type WarningKind ¶
type WarningKind int
WarningKind classifies a Warning from rule analysis.
const ( // WarningKindDead means the rule never matches any tuple in the dimension // Cartesian product. WarningKindDead WarningKind = iota // WarningKindShadowed means the rule matches some tuple but never wins // first-match against earlier rules. WarningKindShadowed // WarningKindAnalysisLimitExceeded means shadowed-rule analysis (Cartesian // enumeration) was skipped because the dimension value product exceeded the // configured limit. Dead-rule detection still runs without this cap. WarningKindAnalysisLimitExceeded )
func (WarningKind) String ¶
func (k WarningKind) String() string
String implements fmt.Stringer for WarningKind.
type WildcardType ¶
type WildcardType struct{}
WildcardType marks a dimension slot as matching any declared value (when dimensions exist) or any input (when the engine has zero dimensions).
var Wildcard WildcardType
Wildcard matches any value in the corresponding dimension. If the engine has no dimensions, it matches any input at that position.