+// MergeStructs merge struct src's value to struct dst
+func MergeStructs(dst, src any) error {
+ vd := reflect.ValueOf(dst)
+ vs := reflect.ValueOf(src)
+ if vd.Kind() != reflect.Ptr {
+ return fmt.Errorf("dst is not a valid pointer.")
+ }
+ if vs.Kind() != reflect.Ptr {
+ return fmt.Errorf("src is not a valid pointer.")
+ }
+
+ vd = vd.Elem()
+ vs = vs.Elem()
+ if vd.Kind() != reflect.Struct {
+ return fmt.Errorf("dst must be a struct.")
+ }
+ if vs.Kind() != reflect.Struct {
+ return fmt.Errorf("src must be a struct.")
+ }
+ if vd.Type() != vs.Type() {
+ return fmt.Errorf("dst and src must be the same type.")
+ }
+
+ for i := range vs.NumField() {
+ srcField, srcFieldType := vs.Field(i), vs.Type().Field(i)
+ dstField := vd.FieldByName(srcFieldType.Name)
+ if !dstField.IsValid() {
+ fmt.Printf("Skipping invalid field: %s\n", srcFieldType.Name)
+ continue
+ }
+ if !dstField.CanSet() {
+ fmt.Printf("Skipping unexported or unsettable field: %s\n", srcFieldType.Name)
+ continue
+ }
+
+ isDstFieldZero := dstField.IsZero()
+ if srcField.Kind() == reflect.Struct {
+ // 如果字段是结构体
+ if isDstFieldZero {
+ // 目标结构体是零值,并且源结构体有值,则直接用源结构体覆盖目标结构体
+ if !srcField.IsZero() {
+ dstField.Set(srcField)
+ }
+ } else {
+ // 目标结构体非零,递归合并其字段
+ dstFieldAddr := dstField.Addr().Interface()
+ srcFieldAddr := srcField.Addr().Interface()
+ if err := MergeStructs(dstFieldAddr, srcFieldAddr); err != nil {
+ return fmt.Errorf("error merging nested struct %s: %w", srcFieldType.Name, err)
+ }
+ }
+ } else if srcField.Kind() == reflect.Ptr && srcField.Elem().Kind() == reflect.Struct {
+ // 如果字段是指向结构体的指针
+ if dstField.IsNil() {
+ // 目标指针是 nil, 并且源指针不是 nil,则复制整个指向的结构体
+ if !srcField.IsNil() {
+ dstField.Set(srcField)
+ }
+ } else if !srcField.IsNil() {
+ // 目标和源指针都不是 nil,递归合并它们指向的结构体
+ if err := MergeStructs(dstField.Interface(), srcField.Interface()); err != nil {
+ return fmt.Errorf("error merging nested ptr struct %s: %w", srcFieldType.Name, err)
+ }
+ }
+ } else {
+ // 对于基本类型或非结构体指针类型
+ if isDstFieldZero {
+ // 如果目标字段是零值,并且源字段不是零值
+ if srcField.IsValid() && srcField.Type().AssignableTo(dstField.Type()) {
+ dstField.Set(srcField)
+ }
+ }
+ }
+ }
+
+ return nil
+}
+