141 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			141 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
| Copyright 2024 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package common
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 
 | |
| 	"github.com/google/cel-go/common/types"
 | |
| 	"github.com/google/cel-go/common/types/ref"
 | |
| 	"github.com/google/cel-go/common/types/traits"
 | |
| )
 | |
| 
 | |
| // ObjectVal is the CEL Val for an object that is constructed via the object
 | |
| // construction syntax.
 | |
| type ObjectVal struct {
 | |
| 	typeRef TypeRef
 | |
| 	fields  map[string]ref.Val
 | |
| }
 | |
| 
 | |
| // NewObjectVal creates an ObjectVal by its TypeRef and its fields.
 | |
| func NewObjectVal(typeRef TypeRef, fields map[string]ref.Val) *ObjectVal {
 | |
| 	return &ObjectVal{
 | |
| 		typeRef: typeRef,
 | |
| 		fields:  fields,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| var _ ref.Val = (*ObjectVal)(nil)
 | |
| var _ traits.Zeroer = (*ObjectVal)(nil)
 | |
| 
 | |
| // ConvertToNative converts the object to map[string]any.
 | |
| // All nested lists are converted into []any native type.
 | |
| //
 | |
| // It returns an error if the target type is not map[string]any,
 | |
| // or any recursive conversion fails.
 | |
| func (v *ObjectVal) ConvertToNative(typeDesc reflect.Type) (any, error) {
 | |
| 	var result map[string]any
 | |
| 	if typeDesc != reflect.TypeOf(result) {
 | |
| 		return nil, fmt.Errorf("unable to convert to %v", typeDesc)
 | |
| 	}
 | |
| 	result = make(map[string]any, len(v.fields))
 | |
| 	for k, v := range v.fields {
 | |
| 		converted, err := convertField(v)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("fail to convert field %q: %w", k, err)
 | |
| 		}
 | |
| 		result[k] = converted
 | |
| 	}
 | |
| 	return result, nil
 | |
| }
 | |
| 
 | |
| // ConvertToType supports type conversions between CEL value types supported by the expression language.
 | |
| func (v *ObjectVal) ConvertToType(typeValue ref.Type) ref.Val {
 | |
| 	switch typeValue {
 | |
| 	case v.typeRef:
 | |
| 		return v
 | |
| 	case types.TypeType:
 | |
| 		return v.typeRef.CELType()
 | |
| 	}
 | |
| 	return types.NewErr("unsupported conversion into %v", typeValue)
 | |
| }
 | |
| 
 | |
| // Equal returns true if the `other` value has the same type and content as the implementing struct.
 | |
| func (v *ObjectVal) Equal(other ref.Val) ref.Val {
 | |
| 	if rhs, ok := other.(*ObjectVal); ok {
 | |
| 		return types.Bool(reflect.DeepEqual(v.fields, rhs.fields))
 | |
| 	}
 | |
| 	return types.Bool(false)
 | |
| }
 | |
| 
 | |
| // Type returns the TypeValue of the value.
 | |
| func (v *ObjectVal) Type() ref.Type {
 | |
| 	return v.typeRef.CELType()
 | |
| }
 | |
| 
 | |
| // Value returns its value as a map[string]any.
 | |
| func (v *ObjectVal) Value() any {
 | |
| 	var result any
 | |
| 	var object map[string]any
 | |
| 	result, err := v.ConvertToNative(reflect.TypeOf(object))
 | |
| 	if err != nil {
 | |
| 		return types.WrapErr(err)
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| // IsZeroValue indicates whether the object is the zero value for the type.
 | |
| // For the ObjectVal, it is zero value if and only if the fields map is empty.
 | |
| func (v *ObjectVal) IsZeroValue() bool {
 | |
| 	return len(v.fields) == 0
 | |
| }
 | |
| 
 | |
| // convertField converts a referred ref.Val to its expected type.
 | |
| // For objects, the expected type is map[string]any
 | |
| // For lists, the expected type is []any
 | |
| // For maps, the expected type is map[string]any
 | |
| // For anything else, it is converted via value.Value()
 | |
| //
 | |
| // It will return an error if the request type is a map but the key
 | |
| // is not a string.
 | |
| func convertField(value ref.Val) (any, error) {
 | |
| 	// special handling for lists, where the elements are converted with Value() instead of ConvertToNative
 | |
| 	// to allow them to become native value of any type.
 | |
| 	if listOfVal, ok := value.Value().([]ref.Val); ok {
 | |
| 		var result []any
 | |
| 		for _, v := range listOfVal {
 | |
| 			result = append(result, v.Value())
 | |
| 		}
 | |
| 		return result, nil
 | |
| 	}
 | |
| 	// unstructured maps, as seen in annotations
 | |
| 	// map keys must be strings
 | |
| 	if mapOfVal, ok := value.Value().(map[ref.Val]ref.Val); ok {
 | |
| 		result := make(map[string]any)
 | |
| 		for k, v := range mapOfVal {
 | |
| 			stringKey, ok := k.Value().(string)
 | |
| 			if !ok {
 | |
| 				return nil, fmt.Errorf("map key %q is of type %t, not string", k, k)
 | |
| 			}
 | |
| 			result[stringKey] = v.Value()
 | |
| 		}
 | |
| 		return result, nil
 | |
| 	}
 | |
| 	return value.Value(), nil
 | |
| }
 |