package policy import ( "fmt" "sync" ) // RuleStore loads policy rules from a backing store. type RuleStore interface { LoadEnabledPolicyRules() ([]Rule, error) } // Engine wraps stateless Evaluate with an in-memory rule cache and // thread-safe access. type Engine struct { mu sync.RWMutex rules []Rule } // NewEngine creates an Engine pre-loaded with the built-in default rules. func NewEngine() *Engine { return &Engine{rules: DefaultRules()} } // SetRules replaces the cached rule set. The provided rules are merged // with the built-in defaults. func (e *Engine) SetRules(rules []Rule) { merged := make([]Rule, 0, len(DefaultRules())+len(rules)) merged = append(merged, DefaultRules()...) merged = append(merged, rules...) e.mu.Lock() e.rules = merged e.mu.Unlock() } // Evaluate runs the policy engine against the cached rule set. func (e *Engine) Evaluate(input PolicyInput) (Effect, *Rule) { e.mu.RLock() rules := make([]Rule, len(e.rules)) copy(rules, e.rules) e.mu.RUnlock() return Evaluate(input, rules) } // Reload loads enabled rules from the store and updates the cache. func (e *Engine) Reload(store RuleStore) error { rules, err := store.LoadEnabledPolicyRules() if err != nil { return fmt.Errorf("policy: reload rules: %w", err) } e.SetRules(rules) return nil }