295 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			295 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2019 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 value
 | |
| 
 | |
| import (
 | |
| 	"encoding/base64"
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| )
 | |
| 
 | |
| // NewValueReflect creates a Value backed by an "interface{}" type,
 | |
| // typically an structured object in Kubernetes world that is uses reflection to expose.
 | |
| // The provided "interface{}" value must be a pointer so that the value can be modified via reflection.
 | |
| // The provided "interface{}" may contain structs and types that are converted to Values
 | |
| // by the jsonMarshaler interface.
 | |
| func NewValueReflect(value interface{}) (Value, error) {
 | |
| 	if value == nil {
 | |
| 		return NewValueInterface(nil), nil
 | |
| 	}
 | |
| 	v := reflect.ValueOf(value)
 | |
| 	if v.Kind() != reflect.Ptr {
 | |
| 		// The root value to reflect on must be a pointer so that map.Set() and map.Delete() operations are possible.
 | |
| 		return nil, fmt.Errorf("value provided to NewValueReflect must be a pointer")
 | |
| 	}
 | |
| 	return wrapValueReflect(v, nil, nil)
 | |
| }
 | |
| 
 | |
| // wrapValueReflect wraps the provide reflect.Value as a value. If parent in the data tree is a map, parentMap
 | |
| // and parentMapKey must be provided so that the returned value may be set and deleted.
 | |
| func wrapValueReflect(value reflect.Value, parentMap, parentMapKey *reflect.Value) (Value, error) {
 | |
| 	val := HeapAllocator.allocValueReflect()
 | |
| 	return val.reuse(value, nil, parentMap, parentMapKey)
 | |
| }
 | |
| 
 | |
| // wrapValueReflect wraps the provide reflect.Value as a value, and panics if there is an error. If parent in the data
 | |
| // tree is a map, parentMap and parentMapKey must be provided so that the returned value may be set and deleted.
 | |
| func mustWrapValueReflect(value reflect.Value, parentMap, parentMapKey *reflect.Value) Value {
 | |
| 	v, err := wrapValueReflect(value, parentMap, parentMapKey)
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| // the value interface doesn't care about the type for value.IsNull, so we can use a constant
 | |
| var nilType = reflect.TypeOf(&struct{}{})
 | |
| 
 | |
| // reuse replaces the value of the valueReflect. If parent in the data tree is a map, parentMap and parentMapKey
 | |
| // must be provided so that the returned value may be set and deleted.
 | |
| func (r *valueReflect) reuse(value reflect.Value, cacheEntry *TypeReflectCacheEntry, parentMap, parentMapKey *reflect.Value) (Value, error) {
 | |
| 	if cacheEntry == nil {
 | |
| 		cacheEntry = TypeReflectEntryOf(value.Type())
 | |
| 	}
 | |
| 	if cacheEntry.CanConvertToUnstructured() {
 | |
| 		u, err := cacheEntry.ToUnstructured(value)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		if u == nil {
 | |
| 			value = reflect.Zero(nilType)
 | |
| 		} else {
 | |
| 			value = reflect.ValueOf(u)
 | |
| 		}
 | |
| 	}
 | |
| 	r.Value = dereference(value)
 | |
| 	r.ParentMap = parentMap
 | |
| 	r.ParentMapKey = parentMapKey
 | |
| 	r.kind = kind(r.Value)
 | |
| 	return r, nil
 | |
| }
 | |
| 
 | |
| // mustReuse replaces the value of the valueReflect and panics if there is an error. If parent in the data tree is a
 | |
| // map, parentMap and parentMapKey must be provided so that the returned value may be set and deleted.
 | |
| func (r *valueReflect) mustReuse(value reflect.Value, cacheEntry *TypeReflectCacheEntry, parentMap, parentMapKey *reflect.Value) Value {
 | |
| 	v, err := r.reuse(value, cacheEntry, parentMap, parentMapKey)
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	return v
 | |
| }
 | |
| 
 | |
| func dereference(val reflect.Value) reflect.Value {
 | |
| 	kind := val.Kind()
 | |
| 	if (kind == reflect.Interface || kind == reflect.Ptr) && !safeIsNil(val) {
 | |
| 		return val.Elem()
 | |
| 	}
 | |
| 	return val
 | |
| }
 | |
| 
 | |
| type valueReflect struct {
 | |
| 	ParentMap    *reflect.Value
 | |
| 	ParentMapKey *reflect.Value
 | |
| 	Value        reflect.Value
 | |
| 	kind         reflectType
 | |
| }
 | |
| 
 | |
| func (r valueReflect) IsMap() bool {
 | |
| 	return r.kind == mapType || r.kind == structMapType
 | |
| }
 | |
| 
 | |
| func (r valueReflect) IsList() bool {
 | |
| 	return r.kind == listType
 | |
| }
 | |
| 
 | |
| func (r valueReflect) IsBool() bool {
 | |
| 	return r.kind == boolType
 | |
| }
 | |
| 
 | |
| func (r valueReflect) IsInt() bool {
 | |
| 	return r.kind == intType || r.kind == uintType
 | |
| }
 | |
| 
 | |
| func (r valueReflect) IsFloat() bool {
 | |
| 	return r.kind == floatType
 | |
| }
 | |
| 
 | |
| func (r valueReflect) IsString() bool {
 | |
| 	return r.kind == stringType || r.kind == byteStringType
 | |
| }
 | |
| 
 | |
| func (r valueReflect) IsNull() bool {
 | |
| 	return r.kind == nullType
 | |
| }
 | |
| 
 | |
| type reflectType = int
 | |
| 
 | |
| const (
 | |
| 	mapType = iota
 | |
| 	structMapType
 | |
| 	listType
 | |
| 	intType
 | |
| 	uintType
 | |
| 	floatType
 | |
| 	stringType
 | |
| 	byteStringType
 | |
| 	boolType
 | |
| 	nullType
 | |
| )
 | |
| 
 | |
| func kind(v reflect.Value) reflectType {
 | |
| 	typ := v.Type()
 | |
| 	rk := typ.Kind()
 | |
| 	switch rk {
 | |
| 	case reflect.Map:
 | |
| 		if v.IsNil() {
 | |
| 			return nullType
 | |
| 		}
 | |
| 		return mapType
 | |
| 	case reflect.Struct:
 | |
| 		return structMapType
 | |
| 	case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8:
 | |
| 		return intType
 | |
| 	case reflect.Uint, reflect.Uint32, reflect.Uint16, reflect.Uint8:
 | |
| 		// Uint64 deliberately excluded, see valueUnstructured.Int.
 | |
| 		return uintType
 | |
| 	case reflect.Float64, reflect.Float32:
 | |
| 		return floatType
 | |
| 	case reflect.String:
 | |
| 		return stringType
 | |
| 	case reflect.Bool:
 | |
| 		return boolType
 | |
| 	case reflect.Slice:
 | |
| 		if v.IsNil() {
 | |
| 			return nullType
 | |
| 		}
 | |
| 		elemKind := typ.Elem().Kind()
 | |
| 		if elemKind == reflect.Uint8 {
 | |
| 			return byteStringType
 | |
| 		}
 | |
| 		return listType
 | |
| 	case reflect.Chan, reflect.Func, reflect.Ptr, reflect.UnsafePointer, reflect.Interface:
 | |
| 		if v.IsNil() {
 | |
| 			return nullType
 | |
| 		}
 | |
| 		panic(fmt.Sprintf("unsupported type: %v", v.Type()))
 | |
| 	default:
 | |
| 		panic(fmt.Sprintf("unsupported type: %v", v.Type()))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TODO find a cleaner way to avoid panics from reflect.IsNil()
 | |
| func safeIsNil(v reflect.Value) bool {
 | |
| 	k := v.Kind()
 | |
| 	switch k {
 | |
| 	case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
 | |
| 		return v.IsNil()
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func (r valueReflect) AsMap() Map {
 | |
| 	return r.AsMapUsing(HeapAllocator)
 | |
| }
 | |
| 
 | |
| func (r valueReflect) AsMapUsing(a Allocator) Map {
 | |
| 	switch r.kind {
 | |
| 	case structMapType:
 | |
| 		v := a.allocStructReflect()
 | |
| 		v.valueReflect = r
 | |
| 		return v
 | |
| 	case mapType:
 | |
| 		v := a.allocMapReflect()
 | |
| 		v.valueReflect = r
 | |
| 		return v
 | |
| 	default:
 | |
| 		panic("value is not a map or struct")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (r valueReflect) AsList() List {
 | |
| 	return r.AsListUsing(HeapAllocator)
 | |
| }
 | |
| 
 | |
| func (r valueReflect) AsListUsing(a Allocator) List {
 | |
| 	if r.IsList() {
 | |
| 		v := a.allocListReflect()
 | |
| 		v.Value = r.Value
 | |
| 		return v
 | |
| 	}
 | |
| 	panic("value is not a list")
 | |
| }
 | |
| 
 | |
| func (r valueReflect) AsBool() bool {
 | |
| 	if r.IsBool() {
 | |
| 		return r.Value.Bool()
 | |
| 	}
 | |
| 	panic("value is not a bool")
 | |
| }
 | |
| 
 | |
| func (r valueReflect) AsInt() int64 {
 | |
| 	if r.kind == intType {
 | |
| 		return r.Value.Int()
 | |
| 	}
 | |
| 	if r.kind == uintType {
 | |
| 		return int64(r.Value.Uint())
 | |
| 	}
 | |
| 
 | |
| 	panic("value is not an int")
 | |
| }
 | |
| 
 | |
| func (r valueReflect) AsFloat() float64 {
 | |
| 	if r.IsFloat() {
 | |
| 		return r.Value.Float()
 | |
| 	}
 | |
| 	panic("value is not a float")
 | |
| }
 | |
| 
 | |
| func (r valueReflect) AsString() string {
 | |
| 	switch r.kind {
 | |
| 	case stringType:
 | |
| 		return r.Value.String()
 | |
| 	case byteStringType:
 | |
| 		return base64.StdEncoding.EncodeToString(r.Value.Bytes())
 | |
| 	}
 | |
| 	panic("value is not a string")
 | |
| }
 | |
| 
 | |
| func (r valueReflect) Unstructured() interface{} {
 | |
| 	val := r.Value
 | |
| 	switch {
 | |
| 	case r.IsNull():
 | |
| 		return nil
 | |
| 	case val.Kind() == reflect.Struct:
 | |
| 		return structReflect{r}.Unstructured()
 | |
| 	case val.Kind() == reflect.Map:
 | |
| 		return mapReflect{valueReflect: r}.Unstructured()
 | |
| 	case r.IsList():
 | |
| 		return listReflect{r.Value}.Unstructured()
 | |
| 	case r.IsString():
 | |
| 		return r.AsString()
 | |
| 	case r.IsInt():
 | |
| 		return r.AsInt()
 | |
| 	case r.IsBool():
 | |
| 		return r.AsBool()
 | |
| 	case r.IsFloat():
 | |
| 		return r.AsFloat()
 | |
| 	default:
 | |
| 		panic(fmt.Sprintf("value of type %s is not a supported by value reflector", val.Type()))
 | |
| 	}
 | |
| }
 | 
