summaryrefslogtreecommitdiff
path: root/vendor/sigs.k8s.io/structured-merge-diff/v4/value/valuereflect.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/sigs.k8s.io/structured-merge-diff/v4/value/valuereflect.go')
-rw-r--r--vendor/sigs.k8s.io/structured-merge-diff/v4/value/valuereflect.go294
1 files changed, 294 insertions, 0 deletions
diff --git a/vendor/sigs.k8s.io/structured-merge-diff/v4/value/valuereflect.go b/vendor/sigs.k8s.io/structured-merge-diff/v4/value/valuereflect.go
new file mode 100644
index 000000000..05e70deba
--- /dev/null
+++ b/vendor/sigs.k8s.io/structured-merge-diff/v4/value/valuereflect.go
@@ -0,0 +1,294 @@
+/*
+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()))
+ }
+}