k8s.io/apiserver@v0.31.1/pkg/cel/mutation/common/val.go (about) 1 /* 2 Copyright 2024 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 common 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 28 // ObjectVal is the CEL Val for an object that is constructed via the object 29 // construction syntax. 30 type ObjectVal struct { 31 typeRef TypeRef 32 fields map[string]ref.Val 33 } 34 35 // NewObjectVal creates an ObjectVal by its TypeRef and its fields. 36 func NewObjectVal(typeRef TypeRef, fields map[string]ref.Val) *ObjectVal { 37 return &ObjectVal{ 38 typeRef: typeRef, 39 fields: fields, 40 } 41 } 42 43 var _ ref.Val = (*ObjectVal)(nil) 44 var _ traits.Zeroer = (*ObjectVal)(nil) 45 46 // ConvertToNative converts the object to map[string]any. 47 // All nested lists are converted into []any native type. 48 // 49 // It returns an error if the target type is not map[string]any, 50 // or any recursive conversion fails. 51 func (v *ObjectVal) ConvertToNative(typeDesc reflect.Type) (any, error) { 52 var result map[string]any 53 if typeDesc != reflect.TypeOf(result) { 54 return nil, fmt.Errorf("unable to convert to %v", typeDesc) 55 } 56 result = make(map[string]any, len(v.fields)) 57 for k, v := range v.fields { 58 converted, err := convertField(v) 59 if err != nil { 60 return nil, fmt.Errorf("fail to convert field %q: %w", k, err) 61 } 62 result[k] = converted 63 } 64 return result, nil 65 } 66 67 // ConvertToType supports type conversions between CEL value types supported by the expression language. 68 func (v *ObjectVal) ConvertToType(typeValue ref.Type) ref.Val { 69 switch typeValue { 70 case v.typeRef: 71 return v 72 case types.TypeType: 73 return v.typeRef.CELType() 74 } 75 return types.NewErr("unsupported conversion into %v", typeValue) 76 } 77 78 // Equal returns true if the `other` value has the same type and content as the implementing struct. 79 func (v *ObjectVal) Equal(other ref.Val) ref.Val { 80 if rhs, ok := other.(*ObjectVal); ok { 81 return types.Bool(reflect.DeepEqual(v.fields, rhs.fields)) 82 } 83 return types.Bool(false) 84 } 85 86 // Type returns the TypeValue of the value. 87 func (v *ObjectVal) Type() ref.Type { 88 return v.typeRef.CELType() 89 } 90 91 // Value returns its value as a map[string]any. 92 func (v *ObjectVal) Value() any { 93 var result any 94 var object map[string]any 95 result, err := v.ConvertToNative(reflect.TypeOf(object)) 96 if err != nil { 97 return types.WrapErr(err) 98 } 99 return result 100 } 101 102 // IsZeroValue indicates whether the object is the zero value for the type. 103 // For the ObjectVal, it is zero value if and only if the fields map is empty. 104 func (v *ObjectVal) IsZeroValue() bool { 105 return len(v.fields) == 0 106 } 107 108 // convertField converts a referred ref.Val to its expected type. 109 // For objects, the expected type is map[string]any 110 // For lists, the expected type is []any 111 // For maps, the expected type is map[string]any 112 // For anything else, it is converted via value.Value() 113 // 114 // It will return an error if the request type is a map but the key 115 // is not a string. 116 func convertField(value ref.Val) (any, error) { 117 // special handling for lists, where the elements are converted with Value() instead of ConvertToNative 118 // to allow them to become native value of any type. 119 if listOfVal, ok := value.Value().([]ref.Val); ok { 120 var result []any 121 for _, v := range listOfVal { 122 result = append(result, v.Value()) 123 } 124 return result, nil 125 } 126 // unstructured maps, as seen in annotations 127 // map keys must be strings 128 if mapOfVal, ok := value.Value().(map[ref.Val]ref.Val); ok { 129 result := make(map[string]any) 130 for k, v := range mapOfVal { 131 stringKey, ok := k.Value().(string) 132 if !ok { 133 return nil, fmt.Errorf("map key %q is of type %t, not string", k, k) 134 } 135 result[stringKey] = v.Value() 136 } 137 return result, nil 138 } 139 return value.Value(), nil 140 }