Centralize trusted JSON value cloning
This commit is contained in:
97
internal/jsonvalue/trusted.go
Normal file
97
internal/jsonvalue/trusted.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package jsonvalue
|
||||
|
||||
import "reflect"
|
||||
|
||||
func cloneTrustedMap(src map[string]any) map[string]any {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]any, len(src))
|
||||
for k, v := range src {
|
||||
out[k] = CloneTrusted(v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// CloneTrusted detaches a previously accepted value without revalidating it.
|
||||
func CloneTrusted(value any) any {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case map[string]any:
|
||||
return cloneTrustedMap(v)
|
||||
case []any:
|
||||
out := make([]any, len(v))
|
||||
for i, item := range v {
|
||||
out[i] = CloneTrusted(item)
|
||||
}
|
||||
return out
|
||||
case []string:
|
||||
if v == nil {
|
||||
return []string(nil)
|
||||
}
|
||||
return append([]string{}, v...)
|
||||
case []byte:
|
||||
if v == nil {
|
||||
return []byte(nil)
|
||||
}
|
||||
return append([]byte{}, v...)
|
||||
default:
|
||||
return cloneTrustedReflect(reflect.ValueOf(value)).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
func cloneTrustedReflect(value reflect.Value) reflect.Value {
|
||||
if !value.IsValid() {
|
||||
return value
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Interface:
|
||||
if value.IsNil() {
|
||||
return reflect.Zero(value.Type())
|
||||
}
|
||||
copied := cloneTrustedReflect(value.Elem())
|
||||
if copied.IsValid() && copied.Type().AssignableTo(value.Type()) {
|
||||
return copied
|
||||
}
|
||||
out := reflect.New(value.Type()).Elem()
|
||||
out.Set(copied)
|
||||
return out
|
||||
case reflect.Pointer:
|
||||
if value.IsNil() {
|
||||
return reflect.Zero(value.Type())
|
||||
}
|
||||
out := reflect.New(value.Type().Elem())
|
||||
out.Elem().Set(cloneTrustedReflect(value.Elem()))
|
||||
return out
|
||||
case reflect.Map:
|
||||
if value.IsNil() {
|
||||
return reflect.Zero(value.Type())
|
||||
}
|
||||
out := reflect.MakeMapWithSize(value.Type(), value.Len())
|
||||
iter := value.MapRange()
|
||||
for iter.Next() {
|
||||
out.SetMapIndex(cloneTrustedReflect(iter.Key()), cloneTrustedReflect(iter.Value()))
|
||||
}
|
||||
return out
|
||||
case reflect.Slice:
|
||||
if value.IsNil() {
|
||||
return reflect.Zero(value.Type())
|
||||
}
|
||||
out := reflect.MakeSlice(value.Type(), value.Len(), value.Cap())
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
out.Index(i).Set(cloneTrustedReflect(value.Index(i)))
|
||||
}
|
||||
return out
|
||||
case reflect.Array:
|
||||
out := reflect.New(value.Type()).Elem()
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
out.Index(i).Set(cloneTrustedReflect(value.Index(i)))
|
||||
}
|
||||
return out
|
||||
default:
|
||||
return value
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user