mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 09:47:54 +00:00
Travis seems to be having issues pulling deps, so we'll have to check in the vendor directory and prevent the makefile from trying to regenerate it normally.
245 lines
6.6 KiB
Go
245 lines
6.6 KiB
Go
// Copyright 2013 Dario Castañé. All rights reserved.
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Based on src/pkg/reflect/deepequal.go from official
|
|
// golang's stdlib.
|
|
|
|
package mergo
|
|
|
|
import (
|
|
"reflect"
|
|
)
|
|
|
|
func hasExportedField(dst reflect.Value) (exported bool) {
|
|
for i, n := 0, dst.NumField(); i < n; i++ {
|
|
field := dst.Type().Field(i)
|
|
if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
|
|
exported = exported || hasExportedField(dst.Field(i))
|
|
} else {
|
|
exported = exported || len(field.PkgPath) == 0
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
type Config struct {
|
|
Overwrite bool
|
|
AppendSlice bool
|
|
Transformers Transformers
|
|
}
|
|
|
|
type Transformers interface {
|
|
Transformer(reflect.Type) func(dst, src reflect.Value) error
|
|
}
|
|
|
|
// Traverses recursively both values, assigning src's fields values to dst.
|
|
// The map argument tracks comparisons that have already been seen, which allows
|
|
// short circuiting on recursive types.
|
|
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
|
|
overwrite := config.Overwrite
|
|
|
|
if !src.IsValid() {
|
|
return
|
|
}
|
|
if dst.CanAddr() {
|
|
addr := dst.UnsafeAddr()
|
|
h := 17 * addr
|
|
seen := visited[h]
|
|
typ := dst.Type()
|
|
for p := seen; p != nil; p = p.next {
|
|
if p.ptr == addr && p.typ == typ {
|
|
return nil
|
|
}
|
|
}
|
|
// Remember, remember...
|
|
visited[h] = &visit{addr, typ, seen}
|
|
}
|
|
|
|
if config.Transformers != nil && !isEmptyValue(dst) {
|
|
if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
|
|
err = fn(dst, src)
|
|
return
|
|
}
|
|
}
|
|
|
|
switch dst.Kind() {
|
|
case reflect.Struct:
|
|
if hasExportedField(dst) {
|
|
for i, n := 0, dst.NumField(); i < n; i++ {
|
|
if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
|
|
dst.Set(src)
|
|
}
|
|
}
|
|
case reflect.Map:
|
|
if dst.IsNil() && !src.IsNil() {
|
|
dst.Set(reflect.MakeMap(dst.Type()))
|
|
}
|
|
for _, key := range src.MapKeys() {
|
|
srcElement := src.MapIndex(key)
|
|
if !srcElement.IsValid() {
|
|
continue
|
|
}
|
|
dstElement := dst.MapIndex(key)
|
|
switch srcElement.Kind() {
|
|
case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
|
|
if srcElement.IsNil() {
|
|
continue
|
|
}
|
|
fallthrough
|
|
default:
|
|
if !srcElement.CanInterface() {
|
|
continue
|
|
}
|
|
switch reflect.TypeOf(srcElement.Interface()).Kind() {
|
|
case reflect.Struct:
|
|
fallthrough
|
|
case reflect.Ptr:
|
|
fallthrough
|
|
case reflect.Map:
|
|
srcMapElm := srcElement
|
|
dstMapElm := dstElement
|
|
if srcMapElm.CanInterface() {
|
|
srcMapElm = reflect.ValueOf(srcMapElm.Interface())
|
|
if dstMapElm.IsValid() {
|
|
dstMapElm = reflect.ValueOf(dstMapElm.Interface())
|
|
}
|
|
}
|
|
if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil {
|
|
return
|
|
}
|
|
case reflect.Slice:
|
|
srcSlice := reflect.ValueOf(srcElement.Interface())
|
|
|
|
var dstSlice reflect.Value
|
|
if !dstElement.IsValid() || dstElement.IsNil() {
|
|
dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
|
|
} else {
|
|
dstSlice = reflect.ValueOf(dstElement.Interface())
|
|
}
|
|
|
|
if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
|
|
dstSlice = srcSlice
|
|
} else if config.AppendSlice {
|
|
dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
|
|
}
|
|
dst.SetMapIndex(key, dstSlice)
|
|
}
|
|
}
|
|
if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map {
|
|
continue
|
|
}
|
|
|
|
if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dstElement))) {
|
|
if dst.IsNil() {
|
|
dst.Set(reflect.MakeMap(dst.Type()))
|
|
}
|
|
dst.SetMapIndex(key, srcElement)
|
|
}
|
|
}
|
|
case reflect.Slice:
|
|
if !dst.CanSet() {
|
|
break
|
|
}
|
|
if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
|
|
dst.Set(src)
|
|
} else if config.AppendSlice {
|
|
dst.Set(reflect.AppendSlice(dst, src))
|
|
}
|
|
case reflect.Ptr:
|
|
fallthrough
|
|
case reflect.Interface:
|
|
if src.IsNil() {
|
|
break
|
|
}
|
|
if src.Kind() != reflect.Interface {
|
|
if dst.IsNil() || overwrite {
|
|
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
|
|
dst.Set(src)
|
|
}
|
|
} else if src.Kind() == reflect.Ptr {
|
|
if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
|
|
return
|
|
}
|
|
} else if dst.Elem().Type() == src.Type() {
|
|
if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
return ErrDifferentArgumentsTypes
|
|
}
|
|
break
|
|
}
|
|
if dst.IsNil() || overwrite {
|
|
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
|
|
dst.Set(src)
|
|
}
|
|
} else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
|
|
return
|
|
}
|
|
default:
|
|
if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) {
|
|
dst.Set(src)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Merge will fill any empty for value type attributes on the dst struct using corresponding
|
|
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
|
|
// and dst must be a pointer to struct.
|
|
// It won't merge unexported (private) fields and will do recursively any exported field.
|
|
func Merge(dst, src interface{}, opts ...func(*Config)) error {
|
|
return merge(dst, src, opts...)
|
|
}
|
|
|
|
// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
|
|
// non-empty src attribute values.
|
|
// Deprecated: use Merge(…) with WithOverride
|
|
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
|
|
return merge(dst, src, append(opts, WithOverride)...)
|
|
}
|
|
|
|
// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
|
|
func WithTransformers(transformers Transformers) func(*Config) {
|
|
return func(config *Config) {
|
|
config.Transformers = transformers
|
|
}
|
|
}
|
|
|
|
// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
|
|
func WithOverride(config *Config) {
|
|
config.Overwrite = true
|
|
}
|
|
|
|
// WithAppendSlice will make merge append slices instead of overwriting it
|
|
func WithAppendSlice(config *Config) {
|
|
config.AppendSlice = true
|
|
}
|
|
|
|
func merge(dst, src interface{}, opts ...func(*Config)) error {
|
|
var (
|
|
vDst, vSrc reflect.Value
|
|
err error
|
|
)
|
|
|
|
config := &Config{}
|
|
|
|
for _, opt := range opts {
|
|
opt(config)
|
|
}
|
|
|
|
if vDst, vSrc, err = resolveValues(dst, src); err != nil {
|
|
return err
|
|
}
|
|
if vDst.Type() != vSrc.Type() {
|
|
return ErrDifferentArgumentsTypes
|
|
}
|
|
return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
|
|
}
|