Files
b2b/app/utils/mapper/mapper.go
2026-03-10 09:02:57 +01:00

75 lines
2.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package mapper
// Package mapper provides utilities to map fields from one struct to another
// by matching field names (case-insensitive). Unmatched fields are left as
// their zero values.
import (
"fmt"
"reflect"
"strings"
)
// Map copies field values from src into dst by matching field names
// (case-insensitive). Fields in dst that have no counterpart in src
// are left at their zero value.
//
// Both dst and src must be pointers to structs.
// Returns an error if the types do not satisfy those constraints.
func Map(dst, src any) error {
dstVal := reflect.ValueOf(dst)
srcVal := reflect.ValueOf(src)
if dstVal.Kind() != reflect.Ptr || dstVal.Elem().Kind() != reflect.Struct {
return fmt.Errorf("mapper: dst must be a pointer to a struct, got %T", dst)
}
if srcVal.Kind() != reflect.Ptr || srcVal.Elem().Kind() != reflect.Struct {
return fmt.Errorf("mapper: src must be a pointer to a struct, got %T", src)
}
dstElem := dstVal.Elem()
srcElem := srcVal.Elem()
// Build a lookup map of src fields: lowercase name -> field value
srcFields := make(map[string]reflect.Value)
for i := 0; i < srcElem.NumField(); i++ {
f := srcElem.Type().Field(i)
if !f.IsExported() {
continue
}
srcFields[strings.ToLower(f.Name)] = srcElem.Field(i)
}
// Iterate over dst fields and copy matching src values
for i := 0; i < dstElem.NumField(); i++ {
dstField := dstElem.Field(i)
dstType := dstElem.Type().Field(i)
if !dstType.IsExported() || !dstField.CanSet() {
continue
}
srcField, ok := srcFields[strings.ToLower(dstType.Name)]
if !ok {
// No matching src field leave zero value in place
continue
}
if srcField.Type().AssignableTo(dstField.Type()) {
dstField.Set(srcField)
} else if srcField.Type().ConvertibleTo(dstField.Type()) {
dstField.Set(srcField.Convert(dstField.Type()))
}
// If neither assignable nor convertible, the dst field keeps its zero value
}
return nil
}
// MustMap is like Map but panics on error.
func MustMap(dst, src any) {
if err := Map(dst, src); err != nil {
panic(err)
}
}