// Copyright 2020-2026 Project Capsule Authors // SPDX-License-Identifier: Apache-2.0 package cache import ( "sync" capsulev1beta2 "github.com/projectcapsule/capsule/api/v1beta2" celruntime "github.com/projectcapsule/capsule/pkg/runtime/cel" "github.com/projectcapsule/capsule/pkg/runtime/jsonpath" "github.com/projectcapsule/capsule/pkg/runtime/selectors" ) type CompiledTarget struct { capsulev1beta2.CustomQuotaStatusTarget CompiledPath *jsonpath.CompiledJSONPath CompiledCEL *celruntime.CompiledExpression CompiledSelectors []selectors.CompiledSelectorWithFields CompiledConditions []*jsonpath.CompiledJSONPath } type CompiledTargetsCache[K comparable] struct { mu sync.RWMutex data map[K][]CompiledTarget } func NewCompiledTargetsCache[K comparable]() *CompiledTargetsCache[K] { return &CompiledTargetsCache[K]{ data: make(map[K][]CompiledTarget), } } func (c *CompiledTargetsCache[K]) Get(key K) ([]CompiledTarget, bool) { c.mu.RLock() defer c.mu.RUnlock() v, ok := c.data[key] if !ok { return nil, false } out := make([]CompiledTarget, len(v)) copy(out, v) return out, true } func (c *CompiledTargetsCache[K]) Set(key K, value []CompiledTarget) { c.mu.Lock() defer c.mu.Unlock() out := make([]CompiledTarget, len(value)) copy(out, value) c.data[key] = out } func (c *CompiledTargetsCache[K]) GetOrBuild(key K, build func() ([]CompiledTarget, error)) ([]CompiledTarget, error) { c.mu.RLock() v, ok := c.data[key] c.mu.RUnlock() if ok { out := make([]CompiledTarget, len(v)) copy(out, v) return out, nil } c.mu.Lock() defer c.mu.Unlock() if v, ok := c.data[key]; ok { out := make([]CompiledTarget, len(v)) copy(out, v) return out, nil } built, err := build() if err != nil { return nil, err } stored := make([]CompiledTarget, len(built)) copy(stored, built) c.data[key] = stored out := make([]CompiledTarget, len(stored)) copy(out, stored) return out, nil } func (c *CompiledTargetsCache[K]) Delete(key K) bool { c.mu.Lock() defer c.mu.Unlock() _, ok := c.data[key] if ok { delete(c.data, key) } return ok } func (c *CompiledTargetsCache[K]) Reset() { c.mu.Lock() defer c.mu.Unlock() clear(c.data) } func (c *CompiledTargetsCache[K]) Stats() int { c.mu.RLock() defer c.mu.RUnlock() return len(c.data) } func (c *CompiledTargetsCache[K]) PruneActive(active map[K]struct{}) int { c.mu.Lock() defer c.mu.Unlock() pruned := 0 for key := range c.data { if _, ok := active[key]; !ok { delete(c.data, key) pruned++ } } return pruned }