k8s.io/apiserver@v0.31.1/pkg/cel/lazy/lazy.go (about) 1 /* 2 Copyright 2023 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package lazy 18 19 import ( 20 "fmt" 21 "reflect" 22 23 "github.com/google/cel-go/common/types" 24 "github.com/google/cel-go/common/types/ref" 25 "github.com/google/cel-go/common/types/traits" 26 27 "k8s.io/apiserver/pkg/cel" 28 ) 29 30 type GetFieldFunc func(*MapValue) ref.Val 31 32 var _ ref.Val = (*MapValue)(nil) 33 var _ traits.Mapper = (*MapValue)(nil) 34 35 // MapValue is a map that lazily evaluate its value when a field is first accessed. 36 // The map value is not designed to be thread-safe. 37 type MapValue struct { 38 typeValue *types.Type 39 40 // values are previously evaluated values obtained from callbacks 41 values map[string]ref.Val 42 // callbacks are a map of field name to the function that returns the field Val 43 callbacks map[string]GetFieldFunc 44 // knownValues are registered names, used for iteration 45 knownValues []string 46 } 47 48 func NewMapValue(objectType ref.Type) *MapValue { 49 return &MapValue{ 50 typeValue: types.NewTypeValue(objectType.TypeName(), traits.IndexerType|traits.FieldTesterType|traits.IterableType), 51 values: map[string]ref.Val{}, 52 callbacks: map[string]GetFieldFunc{}, 53 } 54 } 55 56 // Append adds the given field with its name and callback. 57 func (m *MapValue) Append(name string, callback GetFieldFunc) { 58 m.knownValues = append(m.knownValues, name) 59 m.callbacks[name] = callback 60 } 61 62 // Contains checks if the key is known to the map 63 func (m *MapValue) Contains(key ref.Val) ref.Val { 64 v, found := m.Find(key) 65 if v != nil && types.IsUnknownOrError(v) { 66 return v 67 } 68 return types.Bool(found) 69 } 70 71 // Iterator returns an iterator to traverse the map. 72 func (m *MapValue) Iterator() traits.Iterator { 73 return &iterator{parent: m, index: 0} 74 } 75 76 // Size returns the number of currently known fields 77 func (m *MapValue) Size() ref.Val { 78 return types.Int(len(m.callbacks)) 79 } 80 81 // ConvertToNative returns an error because it is disallowed 82 func (m *MapValue) ConvertToNative(typeDesc reflect.Type) (any, error) { 83 return nil, fmt.Errorf("disallowed conversion from %q to %q", m.typeValue.TypeName(), typeDesc.Name()) 84 } 85 86 // ConvertToType converts the map to the given type. 87 // Only its own type and "Type" type are allowed. 88 func (m *MapValue) ConvertToType(typeVal ref.Type) ref.Val { 89 switch typeVal { 90 case m.typeValue: 91 return m 92 case types.TypeType: 93 return m.typeValue 94 } 95 return types.NewErr("disallowed conversion from %q to %q", m.typeValue.TypeName(), typeVal.TypeName()) 96 } 97 98 // Equal returns true if the other object is the same pointer-wise. 99 func (m *MapValue) Equal(other ref.Val) ref.Val { 100 otherMap, ok := other.(*MapValue) 101 if !ok { 102 return types.MaybeNoSuchOverloadErr(other) 103 } 104 return types.Bool(m == otherMap) 105 } 106 107 // Type returns its registered type. 108 func (m *MapValue) Type() ref.Type { 109 return m.typeValue 110 } 111 112 // Value is not allowed. 113 func (m *MapValue) Value() any { 114 return types.NoSuchOverloadErr() 115 } 116 117 // resolveField resolves the field. Calls the callback if the value is not yet stored. 118 func (m *MapValue) resolveField(name string) ref.Val { 119 v, seen := m.values[name] 120 if seen { 121 return v 122 } 123 f := m.callbacks[name] 124 v = f(m) 125 m.values[name] = v 126 return v 127 } 128 129 func (m *MapValue) Find(key ref.Val) (ref.Val, bool) { 130 n, ok := key.(types.String) 131 if !ok { 132 return types.MaybeNoSuchOverloadErr(n), true 133 } 134 name, ok := cel.Unescape(n.Value().(string)) 135 if !ok { 136 return nil, false 137 } 138 if _, exists := m.callbacks[name]; !exists { 139 return nil, false 140 } 141 return m.resolveField(name), true 142 } 143 144 func (m *MapValue) Get(key ref.Val) ref.Val { 145 v, found := m.Find(key) 146 if found { 147 return v 148 } 149 return types.ValOrErr(key, "no such key: %v", key) 150 } 151 152 type iterator struct { 153 parent *MapValue 154 index int 155 } 156 157 func (i *iterator) ConvertToNative(typeDesc reflect.Type) (any, error) { 158 return nil, fmt.Errorf("disallowed conversion to %q", typeDesc.Name()) 159 } 160 161 func (i *iterator) ConvertToType(typeValue ref.Type) ref.Val { 162 return types.NewErr("disallowed conversion o %q", typeValue.TypeName()) 163 } 164 165 func (i *iterator) Equal(other ref.Val) ref.Val { 166 otherIterator, ok := other.(*iterator) 167 if !ok { 168 return types.MaybeNoSuchOverloadErr(other) 169 } 170 return types.Bool(otherIterator == i) 171 } 172 173 func (i *iterator) Type() ref.Type { 174 return types.IteratorType 175 } 176 177 func (i *iterator) Value() any { 178 return nil 179 } 180 181 func (i *iterator) HasNext() ref.Val { 182 return types.Bool(i.index < len(i.parent.knownValues)) 183 } 184 185 func (i *iterator) Next() ref.Val { 186 ret := i.parent.Get(types.String(i.parent.knownValues[i.index])) 187 i.index++ 188 return ret 189 } 190 191 var _ traits.Iterator = (*iterator)(nil)