diff --git a/pkg/nsrule/env.go b/pkg/nsrule/env.go index 2d0e9ae..a8ab312 100644 --- a/pkg/nsrule/env.go +++ b/pkg/nsrule/env.go @@ -1,18 +1,45 @@ package nsrule -import "github.com/antonmedv/expr" +import ( + "maps" +) // Env contains data used to evaluate rules. -type Env struct { - env map[string]any -} +type Env map[string]any -var dummyEnv = expr.Env(NewEnv().env) +var ( + dummyEnv = Env{} + defaultEnv = Env{} +) -// NewEnv initializes an env using the provided information. -// -// TODO +// NewEnv shallow-copies the default values into a new Env. func NewEnv() Env { - env := map[string]any{} - return Env{env} + return maps.Clone(defaultEnv) +} + +func define[T any](name string, def T, optional bool) func(Env, T) { + if name == "" { + panic("define: name is required") + } + if _, ok := dummyEnv[name]; ok { + panic("define: name is already used") + } + dummyEnv[name] = def + if !optional { + defaultEnv[name] = def + } + return func(e Env, v T) { e[name] = v } +} + +// Define registers an optional variable with the provided name. For parse-time +// expression type-checking to work, T should not be any. +func Define[T any](name string) func(Env, T) { + var zero T + return define[T](name, zero, true) +} + +// DefineDefault registers a variable with the provided name. For parse-time +// expression type-checking to work, T should not be any. +func DefineDefault[T any](name string, def T) func(Env, T) { + return define[T](name, def, false) } diff --git a/pkg/nsrule/rule.go b/pkg/nsrule/rule.go index 7b9238c..4beac2a 100644 --- a/pkg/nsrule/rule.go +++ b/pkg/nsrule/rule.go @@ -171,7 +171,7 @@ func ParseRules(r io.Reader, name string) ([]Rule, error) { name: name, line: expN, } - if v, err := expr.Compile(expB.String(), expr.AsBool(), expr.Optimize(true), dummyEnv); err != nil { // TODO: dummy env + if v, err := expr.Compile(expB.String(), expr.AsBool(), expr.Optimize(true), expr.Env(dummyEnv)); err != nil { // TODO: dummy env return rs, fmt.Errorf("line %d: compile rule expression: %w", expN, err) } else { r.expr = v