initial commit
This commit is contained in:
196
pkg/reflectutil/structpopulate.go
Normal file
196
pkg/reflectutil/structpopulate.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package reflectutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ValueGetter wraps a map[string]any and implements ValueGetter
|
||||
type ValueGetter map[string]any
|
||||
|
||||
// Get retrieves a value from the map by key
|
||||
func (m ValueGetter) Get(key string) (any, bool) {
|
||||
v, ok := m[key]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// GetFloat64 retrieves a float64 value from the map by key
|
||||
func (m ValueGetter) GetFloat64(key string) (float64, bool) {
|
||||
v, ok := m.Get(key)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
switch val := v.(type) {
|
||||
case float64:
|
||||
return val, true
|
||||
case float32:
|
||||
return float64(val), true
|
||||
case int:
|
||||
return float64(val), true
|
||||
case int64:
|
||||
return float64(val), true
|
||||
case int32:
|
||||
return float64(val), true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
// GetInt retrieves an int value from the map by key
|
||||
func (m ValueGetter) GetInt(key string) (int, bool) {
|
||||
v, ok := m.Get(key)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
switch val := v.(type) {
|
||||
case int:
|
||||
return val, true
|
||||
case int64:
|
||||
return int(val), true
|
||||
case int32:
|
||||
return int(val), true
|
||||
case float64:
|
||||
return int(val), true
|
||||
case float32:
|
||||
return int(val), true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
// GetString retrieves a string value from the map by key
|
||||
func (m ValueGetter) GetString(key string) (string, bool) {
|
||||
v, ok := m.Get(key)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
return val, true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
// GetJSONTagName extracts the JSON tag name from a struct field
|
||||
func GetJSONTagName(field reflect.StructField) string {
|
||||
tag := field.Tag.Get("json")
|
||||
if tag == "" || tag == "-" {
|
||||
return ""
|
||||
}
|
||||
// Handle cases like `json:"name,omitempty"` - take only the first part
|
||||
if idx := strings.Index(tag, ","); idx != -1 {
|
||||
tag = tag[:idx]
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
// getFloat64FromAny converts an any value to float64
|
||||
func getFloat64FromAny(v any) (float64, bool) {
|
||||
switch val := v.(type) {
|
||||
case float64:
|
||||
return val, true
|
||||
case float32:
|
||||
return float64(val), true
|
||||
case int:
|
||||
return float64(val), true
|
||||
case int64:
|
||||
return float64(val), true
|
||||
case int32:
|
||||
return float64(val), true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
// getIntFromAny converts an any value to int
|
||||
func getIntFromAny(v any) (int, bool) {
|
||||
switch val := v.(type) {
|
||||
case int:
|
||||
return val, true
|
||||
case int64:
|
||||
return int(val), true
|
||||
case int32:
|
||||
return int(val), true
|
||||
case float64:
|
||||
return int(val), true
|
||||
case float32:
|
||||
return int(val), true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
// getStringFromAny converts an any value to string
|
||||
func getStringFromAny(v any) (string, bool) {
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
return val, true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
// PopulateStructFromMap populates a struct from a map[string]any using reflection
|
||||
// The target must be a pointer to a struct
|
||||
func PopulateStructFromMap(m map[string]any, target interface{}) error {
|
||||
v := reflect.ValueOf(target)
|
||||
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
|
||||
return fmt.Errorf("target must be a pointer to a struct")
|
||||
}
|
||||
|
||||
v = v.Elem()
|
||||
t := v.Type()
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := v.Field(i)
|
||||
fieldType := t.Field(i)
|
||||
|
||||
if !field.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
jsonTag := GetJSONTagName(fieldType)
|
||||
if jsonTag == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
mapVal, ok := m[jsonTag]
|
||||
if !ok {
|
||||
return fmt.Errorf("%s not found in map", jsonTag)
|
||||
}
|
||||
|
||||
fieldKind := field.Kind()
|
||||
switch fieldKind {
|
||||
case reflect.Float64:
|
||||
val, ok := getFloat64FromAny(mapVal)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot convert %s to float64 for field %s", reflect.TypeOf(mapVal), jsonTag)
|
||||
}
|
||||
field.SetFloat(val)
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
val, ok := getIntFromAny(mapVal)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot convert %s to int for field %s", reflect.TypeOf(mapVal), jsonTag)
|
||||
}
|
||||
field.SetInt(int64(val))
|
||||
|
||||
case reflect.String:
|
||||
val, ok := getStringFromAny(mapVal)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot convert %s to string for field %s", reflect.TypeOf(mapVal), jsonTag)
|
||||
}
|
||||
field.SetString(val)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported field type %s for field %s", fieldKind, jsonTag)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user