mirror of
https://github.com/hanzoai/pack.git
synced 2026-09-30 23:43:13 +00:00
135 lines
2.9 KiB
Go
135 lines
2.9 KiB
Go
package utils
|
|
|
|
import (
|
|
"reflect"
|
|
)
|
|
|
|
// MergeStructs merges multiple structs of the same type, with later values taking precedence.
|
|
// Only non-zero values from later structs will override earlier values.
|
|
func MergeStructs(dst interface{}, srcs ...interface{}) {
|
|
for _, src := range srcs {
|
|
if src == nil {
|
|
continue
|
|
}
|
|
mergeStruct(dst, src)
|
|
}
|
|
}
|
|
|
|
// mergeStruct merges two structs of the same type, taking non-zero values from src
|
|
func mergeStruct(dst, src interface{}) {
|
|
dstValue := reflect.ValueOf(dst).Elem()
|
|
srcValue := reflect.ValueOf(src)
|
|
|
|
if srcValue.Kind() == reflect.Ptr {
|
|
srcValue = srcValue.Elem()
|
|
}
|
|
|
|
// If it's not a struct, we can't iterate over fields
|
|
if srcValue.Kind() != reflect.Struct {
|
|
return
|
|
}
|
|
|
|
for i := 0; i < srcValue.NumField(); i++ {
|
|
srcField := srcValue.Field(i)
|
|
dstField := dstValue.Field(i)
|
|
|
|
switch srcField.Kind() {
|
|
case reflect.Map:
|
|
mergeMap(dstField, srcField)
|
|
|
|
case reflect.Slice:
|
|
// This is specific behaviour for how we want to merge slices
|
|
// Always override with the non-nil slice
|
|
if !srcField.IsNil() {
|
|
dstField.Set(srcField)
|
|
}
|
|
|
|
case reflect.Ptr:
|
|
mergePtr(dstField, srcField)
|
|
|
|
case reflect.Struct:
|
|
mergeStruct(dstField.Addr().Interface(), srcField.Interface())
|
|
|
|
default:
|
|
if !isZeroValue(srcField) {
|
|
dstField.Set(srcField)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func mergeMap(dst, src reflect.Value) {
|
|
if src.IsNil() {
|
|
return
|
|
}
|
|
|
|
if dst.IsNil() {
|
|
dst.Set(reflect.MakeMap(src.Type()))
|
|
}
|
|
|
|
for _, key := range src.MapKeys() {
|
|
srcValue := src.MapIndex(key)
|
|
dstValue := dst.MapIndex(key)
|
|
|
|
if srcValue.Kind() == reflect.Ptr && srcValue.Elem().Kind() == reflect.Struct {
|
|
if !dstValue.IsValid() {
|
|
dst.SetMapIndex(key, srcValue)
|
|
} else {
|
|
mergeStruct(dstValue.Interface(), srcValue.Interface())
|
|
}
|
|
continue
|
|
}
|
|
|
|
dst.SetMapIndex(key, srcValue)
|
|
}
|
|
}
|
|
|
|
func mergePtr(dst, src reflect.Value) {
|
|
if src.IsNil() {
|
|
return
|
|
}
|
|
|
|
switch src.Elem().Kind() {
|
|
case reflect.Struct:
|
|
if dst.IsNil() {
|
|
dst.Set(reflect.New(src.Elem().Type()))
|
|
}
|
|
mergeStruct(dst.Interface(), src.Interface())
|
|
|
|
case reflect.Slice:
|
|
if dst.IsNil() || !src.Elem().IsNil() {
|
|
dst.Set(src)
|
|
}
|
|
|
|
default:
|
|
dst.Set(src)
|
|
}
|
|
}
|
|
|
|
// isZeroValue checks if a reflect.Value is its zero value
|
|
func isZeroValue(v reflect.Value) bool {
|
|
switch v.Kind() {
|
|
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
|
return v.Len() == 0
|
|
case reflect.Bool:
|
|
return !v.Bool()
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
return v.Int() == 0
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
return v.Uint() == 0
|
|
case reflect.Float32, reflect.Float64:
|
|
return v.Float() == 0
|
|
case reflect.Interface, reflect.Ptr:
|
|
return v.IsNil()
|
|
case reflect.Struct:
|
|
// For structs, check if all fields are zero values
|
|
for i := 0; i < v.NumField(); i++ {
|
|
if !isZeroValue(v.Field(i)) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|