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 }