197 lines
4.0 KiB
Go
197 lines
4.0 KiB
Go
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
|
|
}
|