Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 31 additions & 45 deletions automapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,87 +29,75 @@ import (
// It is a design decision to panic when a field cannot be mapped in the
// destination to ensure that a renamed field in either the source or
// destination does not result in subtle silent bug.
func Map(source, dest interface{}) {
var destType = reflect.TypeOf(dest)
if destType.Kind() != reflect.Ptr {
panic("Dest must be a pointer type")
}
var sourceVal = reflect.ValueOf(source)
var destVal = reflect.ValueOf(dest).Elem()
mapValues(sourceVal, destVal, false)
}

// MapLoose works just like Map, except it doesn't fail when the destination
// type contains fields not supplied by the source.
//
// This function is meant to be a temporary solution - the general idea is
// that the Map function should take a number of options that can modify its
// behavior - but I'd rather not add that functionality before I have a better
// idea what is a good options format.
func MapLoose(source, dest interface{}) {
var destType = reflect.TypeOf(dest)
if destType.Kind() != reflect.Ptr {
panic("Dest must be a pointer type")
}
func Map[T any](source any) T {
// do like this des := Map[struct{ Children []testClass2 }](source)
var dest *T = new(T)

var sourceVal = reflect.ValueOf(source)
var destVal = reflect.ValueOf(dest).Elem()
mapValues(sourceVal, destVal, true)
mapValues(sourceVal, destVal)

return *dest

}

func mapValues(sourceVal, destVal reflect.Value, loose bool) {
destType := destVal.Type()
if destType.Kind() == reflect.Struct {
func mapValues(sourceVal, destVal reflect.Value) {
sourceType := sourceVal.Type()
if sourceType.Kind() == reflect.Struct {
if sourceVal.Type().Kind() == reflect.Ptr {
if sourceVal.IsNil() {
// If source is nil, it maps to an empty struct
sourceVal = reflect.New(sourceVal.Type().Elem())
}
sourceVal = sourceVal.Elem()
}
for i := 0; i < destVal.NumField(); i++ {
mapField(sourceVal, destVal, i, loose)
for i := 0; i < sourceType.NumField(); i++ {
mapField(sourceVal, destVal, i)
}
} else if destType == sourceVal.Type() {
} else if sourceType.Kind() == reflect.Slice {
mapSlice(sourceVal, destVal)
} else if sourceType == sourceVal.Type() {
destVal.Set(sourceVal)
} else if destType.Kind() == reflect.Ptr {
} else if sourceType.Kind() == reflect.Ptr {
if valueIsNil(sourceVal) {
return
}
val := reflect.New(destType.Elem())
mapValues(sourceVal, val.Elem(), loose)
val := reflect.New(sourceType.Elem())
mapValues(sourceVal, val.Elem())
destVal.Set(val)
} else if destType.Kind() == reflect.Slice {
mapSlice(sourceVal, destVal, loose)
} else {
panic("Currently not supported")
}

}

func mapSlice(sourceVal, destVal reflect.Value, loose bool) {
func mapSlice(sourceVal, destVal reflect.Value) {
destType := destVal.Type()
length := sourceVal.Len()
target := reflect.MakeSlice(destType, length, length)
for j := 0; j < length; j++ {
val := reflect.New(destType.Elem()).Elem()
mapValues(sourceVal.Index(j), val, loose)
mapValues(sourceVal.Index(j), val)
target.Index(j).Set(val)
}

if length == 0 {
verifyArrayTypesAreCompatible(sourceVal, destVal, loose)
verifyArrayTypesAreCompatible(sourceVal, destVal)
}
destVal.Set(target)
}

func verifyArrayTypesAreCompatible(sourceVal, destVal reflect.Value, loose bool) {
func verifyArrayTypesAreCompatible(sourceVal, destVal reflect.Value) {
dummyDest := reflect.New(reflect.PtrTo(destVal.Type()))
dummySource := reflect.MakeSlice(sourceVal.Type(), 1, 1)
mapValues(dummySource, dummyDest.Elem(), loose)
mapValues(dummySource, dummyDest.Elem())
}

func mapField(source, destVal reflect.Value, i int, loose bool) {
func mapField(source, destVal reflect.Value, i int) {
destType := destVal.Type()
fieldName := destType.Field(i).Name

defer func() {
if r := recover(); r != nil {
panic(fmt.Sprintf("Error mapping field: %s. DestType: %v. SourceType: %v. Error: %v", fieldName, destType, source.Type(), r))
Expand All @@ -118,18 +106,16 @@ func mapField(source, destVal reflect.Value, i int, loose bool) {

destField := destVal.Field(i)
if destType.Field(i).Anonymous {
mapValues(source, destField, loose)
mapValues(source, destField)
} else {
if valueIsContainedInNilEmbeddedType(source, fieldName) {
return
}
sourceField := source.FieldByName(fieldName)
if (sourceField == reflect.Value{}) {
if loose {
return
}

if destField.Kind() == reflect.Struct {
mapValues(source, destField, loose)
mapValues(source, destField)
return
} else {
for i := 0; i < source.NumField(); i++ {
Expand All @@ -142,7 +128,7 @@ func mapField(source, destVal reflect.Value, i int, loose bool) {
}
}
}
mapValues(sourceField, destField, loose)
mapValues(sourceField, destField)
}
}

Expand Down