82 lines
2.1 KiB
Go
82 lines
2.1 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
// Clone only configuration data. json:"-" capabilities remain borrowed and
|
|
// are never traversed (log writers, clients and mutex-bearing runtime objects).
|
|
// Unexported immutable implementation details such as time.Time are copied.
|
|
func cloneConfiguration(v reflect.Value, depth int) (reflect.Value, error) {
|
|
if depth > 64 {
|
|
return reflect.Value{}, errors.New("config: defaults contain cycles or excessive nesting")
|
|
}
|
|
result := reflect.New(v.Type()).Elem()
|
|
result.Set(v)
|
|
switch v.Kind() {
|
|
case reflect.Struct:
|
|
for i := 0; i < v.NumField(); i++ {
|
|
field := v.Type().Field(i)
|
|
if field.PkgPath != "" || strings.Split(field.Tag.Get("json"), ",")[0] == "-" {
|
|
continue
|
|
}
|
|
child, err := cloneConfiguration(v.Field(i), depth+1)
|
|
if err != nil {
|
|
return reflect.Value{}, err
|
|
}
|
|
result.Field(i).Set(child)
|
|
}
|
|
case reflect.Pointer:
|
|
if !v.IsNil() {
|
|
child, err := cloneConfiguration(v.Elem(), depth+1)
|
|
if err != nil {
|
|
return reflect.Value{}, err
|
|
}
|
|
result.Set(reflect.New(v.Type().Elem()))
|
|
result.Elem().Set(child)
|
|
}
|
|
case reflect.Interface:
|
|
if !v.IsNil() {
|
|
child, err := cloneConfiguration(v.Elem(), depth+1)
|
|
if err != nil {
|
|
return reflect.Value{}, err
|
|
}
|
|
result.Set(child)
|
|
}
|
|
case reflect.Slice:
|
|
if !v.IsNil() {
|
|
result = reflect.MakeSlice(v.Type(), v.Len(), v.Len())
|
|
for i := 0; i < v.Len(); i++ {
|
|
child, err := cloneConfiguration(v.Index(i), depth+1)
|
|
if err != nil {
|
|
return reflect.Value{}, err
|
|
}
|
|
result.Index(i).Set(child)
|
|
}
|
|
}
|
|
case reflect.Array:
|
|
for i := 0; i < v.Len(); i++ {
|
|
child, err := cloneConfiguration(v.Index(i), depth+1)
|
|
if err != nil {
|
|
return reflect.Value{}, err
|
|
}
|
|
result.Index(i).Set(child)
|
|
}
|
|
case reflect.Map:
|
|
if !v.IsNil() {
|
|
result = reflect.MakeMapWithSize(v.Type(), v.Len())
|
|
iter := v.MapRange()
|
|
for iter.Next() {
|
|
child, err := cloneConfiguration(iter.Value(), depth+1)
|
|
if err != nil {
|
|
return reflect.Value{}, err
|
|
}
|
|
result.SetMapIndex(iter.Key(), child)
|
|
}
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|