// Copyright 2020-2026 Project Capsule Authors // SPDX-License-Identifier: Apache-2.0 package cache import ( "fmt" "sync" "k8s.io/apiserver/pkg/cel/environment" celruntime "github.com/projectcapsule/capsule/pkg/runtime/cel" ) type celCacheKey struct { expression string resultType celruntime.ResultType mode environment.Type } type CELCache struct { mu sync.RWMutex compiler *celruntime.Compiler data map[celCacheKey]*celruntime.CompiledExpression } func NewCELCache() (*CELCache, error) { compiler, err := celruntime.NewCompiler() if err != nil { return nil, err } return &CELCache{ compiler: compiler, data: make(map[celCacheKey]*celruntime.CompiledExpression), }, nil } func (c *CELCache) GetOrCompileBoolean( expression string, mode environment.Type, ) (*celruntime.CompiledExpression, error) { return c.getOrCompile(expression, celruntime.ResultTypeBoolean, mode) } func (c *CELCache) GetOrCompileQuantity( expression string, mode environment.Type, ) (*celruntime.CompiledExpression, error) { return c.getOrCompile(expression, celruntime.ResultTypeQuantity, mode) } func (c *CELCache) DeleteMany(expressions ...string) int { if c == nil { return 0 } c.mu.Lock() defer c.mu.Unlock() deleted := 0 for key := range c.data { for _, expression := range expressions { if expression != "" && key.expression == expression { delete(c.data, key) deleted++ break } } } return deleted } func (c *CELCache) Stats() int { if c == nil { return 0 } c.mu.RLock() defer c.mu.RUnlock() return len(c.data) } func (c *CELCache) getOrCompile( expression string, resultType celruntime.ResultType, mode environment.Type, ) (*celruntime.CompiledExpression, error) { if c == nil || c.compiler == nil { return nil, fmt.Errorf("CEL cache is nil") } key := celCacheKey{ expression: expression, resultType: resultType, mode: mode, } c.mu.RLock() compiled, ok := c.data[key] c.mu.RUnlock() if ok { return compiled, nil } c.mu.Lock() defer c.mu.Unlock() if compiled, ok = c.data[key]; ok { return compiled, nil } var err error switch resultType { case celruntime.ResultTypeBoolean: compiled, err = c.compiler.CompileBoolean(expression, mode) case celruntime.ResultTypeQuantity: compiled, err = c.compiler.CompileQuantity(expression, mode) default: err = fmt.Errorf("unsupported CEL result type %q", resultType) } if err != nil { return nil, err } c.data[key] = compiled return compiled, nil }