Files
zeekayandHanzo Dev 4be00f32ab Go 1.27.1, and the code it can say more simply
Modules declare go 1.27.1 and the build images follow. go fix applied the
modernizers: any, reflect.TypeFor, the atomic types, WaitGroup.Go, the
reflect field iterators, CutPrefix, SplitSeq, strings.Builder and range over
int. omitzero is not applied, since it changes what the JSON says.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-16 19:46:12 -07:00

105 lines
2.1 KiB
Go

package reflect
import "reflect"
// Copy copies fields from src to dst using reflection.
// Supports struct-to-struct and slice-to-slice copying.
func Copy(src any, dst any) (err error) {
var (
isSlice bool
fromType reflect.Type
isFromPtr bool
toType reflect.Type
isToPtr bool
amount int
)
from := reflect.Indirect(reflect.ValueOf(src))
to := reflect.Indirect(reflect.ValueOf(dst))
if to.Kind() == reflect.Slice {
isSlice = true
if from.Kind() == reflect.Slice {
fromType = from.Type().Elem()
if fromType.Kind() == reflect.Pointer {
fromType = fromType.Elem()
isFromPtr = true
}
amount = from.Len()
} else {
fromType = from.Type()
amount = 1
}
toType = to.Type().Elem()
if toType.Kind() == reflect.Pointer {
toType = toType.Elem()
isToPtr = true
}
} else {
fromType = from.Type()
toType = to.Type()
amount = 1
}
for e := 0; e < amount; e++ {
var dest, source reflect.Value
if isSlice {
if from.Kind() == reflect.Slice {
source = from.Index(e)
if isFromPtr {
source = source.Elem()
}
} else {
source = from
}
} else {
source = from
}
if isSlice {
dest = reflect.New(toType).Elem()
} else {
dest = to
}
for field := range fromType.Fields() {
if !field.Anonymous {
name := field.Name
fromField := source.FieldByName(name)
toField := dest.FieldByName(name)
toMethod := dest.Addr().MethodByName(name)
if fromField.IsValid() && toField.IsValid() {
toField.Set(fromField)
}
if fromField.IsValid() && toMethod.IsValid() {
toMethod.Call([]reflect.Value{fromField})
}
}
}
for field := range toType.Fields() {
if !field.Anonymous {
name := field.Name
fromMethod := source.Addr().MethodByName(name)
toField := dest.FieldByName(name)
if fromMethod.IsValid() && toField.IsValid() {
values := fromMethod.Call([]reflect.Value{})
if len(values) >= 1 {
toField.Set(values[0])
}
}
}
}
if isSlice {
if isToPtr {
to.Set(reflect.Append(to, dest.Addr()))
} else {
to.Set(reflect.Append(to, dest))
}
}
}
return
}