Files
go-omni/config/clone.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
}