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 }