mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed? - Split implementation of "constrained default" settings from "plain default" settings. This is more code and the diff looks complex, but the individual paths are both simpler than the mixed version. - Add conversion cache using a weak map. - Remove GlobalCachedTypedValue. - Use "raw" values for subscription dispatch deduping to avoid unnecessary conversions. - Deep copy default values when using mapstructure, to avoid problems with merging over shared default values. ## Why? - Fixes #6756 - Performance improvement for "plain default" settings (almost all of them) - Performance improvement for settings with complex converters - Remove footgun in defaults that aren't scalar values ## How did you test it? existing+new unit tests
69 lines
1.9 KiB
Go
69 lines
1.9 KiB
Go
package dynamicconfig
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
// deepCopyForMapstructure does a simple deep copy of T. Fancy cases (anything other than plain old data)
|
|
// is not handled and will panic.
|
|
func deepCopyForMapstructure[T any](t T) T {
|
|
// nolint:revive // this will be triggered from a static initializer before it can be triggered from production code
|
|
return deepCopyValue(reflect.ValueOf(t)).Interface().(T)
|
|
}
|
|
|
|
func deepCopyValue(v reflect.Value) reflect.Value {
|
|
switch v.Kind() {
|
|
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
|
reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.String:
|
|
nv := reflect.New(v.Type()).Elem()
|
|
nv.Set(v)
|
|
return nv
|
|
case reflect.Array:
|
|
nv := reflect.New(v.Type()).Elem()
|
|
for i := range v.Len() {
|
|
nv.Index(i).Set(deepCopyValue(v.Index(i)))
|
|
}
|
|
return nv
|
|
case reflect.Map:
|
|
if v.IsNil() {
|
|
return v
|
|
}
|
|
nv := reflect.MakeMapWithSize(v.Type(), v.Len())
|
|
for i := v.MapRange(); i.Next(); {
|
|
nv.SetMapIndex(i.Key(), deepCopyValue(i.Value()))
|
|
}
|
|
return nv
|
|
case reflect.Pointer:
|
|
if v.IsNil() {
|
|
return v
|
|
}
|
|
return deepCopyValue(v.Elem()).Addr()
|
|
case reflect.Slice:
|
|
if v.IsNil() {
|
|
return v
|
|
}
|
|
nv := reflect.MakeSlice(v.Type(), v.Len(), v.Len())
|
|
for i := range v.Len() {
|
|
nv.Index(i).Set(deepCopyValue(v.Index(i)))
|
|
}
|
|
return nv
|
|
case reflect.Struct:
|
|
nv := reflect.New(v.Type()).Elem()
|
|
for i := range v.Type().NumField() {
|
|
nv.Field(i).Set(deepCopyValue(v.Field(i)))
|
|
}
|
|
return nv
|
|
case reflect.Interface, reflect.Func, reflect.Chan:
|
|
// only nil values of any other reference types allowed!
|
|
if v.IsNil() {
|
|
return v
|
|
}
|
|
fallthrough
|
|
default:
|
|
// nolint:forbidigo // this will be triggered from a static initializer before it can be triggered from production code
|
|
panic(fmt.Sprintf("Can't deep copy value of type %s: %v", v.Type(), v))
|
|
}
|
|
}
|