package mapstruct import ( "errors" "fmt" "reflect" ) // converts a struct to map[string]any. func ToMap(input any, opts ...Option) (map[string]any, error) { options := defaultOptions() for _, opt := range opts { opt(options) } v := reflect.ValueOf(input) if v.Kind() == reflect.Ptr { if v.IsNil() { return nil, errors.New("input is nil pointer") } v = v.Elem() } if v.Kind() != reflect.Struct { return nil, errors.New("input is not a struct") } return convertStructToMap(v, options) } // convertStructToMap performs the actual struct-to-map conversion. func convertStructToMap(v reflect.Value, opts *Options) (map[string]any, error) { result := make(map[string]any) t := v.Type() for i := 0; i < v.NumField(); i++ { field := t.Field(i) fieldValue := v.Field(i) if !field.IsExported() { continue } name, skip := getFieldName(field, opts.TagName) if skip { continue } value, err := convertField(fieldValue, field, opts) if err != nil { return nil, fmt.Errorf("field %s: %w", name, err) } if value != nil { result[name] = value } } return result, nil } // convertField converts a single struct field. func convertField(v reflect.Value, field reflect.StructField, opts *Options) (any, error) { // Apply hooks if result, handled, err := applyStructToMapHook(v, field, opts); handled { return result, err } // Handle pointers if v.Kind() == reflect.Ptr { if v.IsNil() { return nil, nil } v = v.Elem() } // Handle nested structs if v.Kind() == reflect.Struct { return convertStructToMap(v, opts) } // Handle slices/arrays if v.Kind() == reflect.Slice || v.Kind() == reflect.Array { return convertSlice(v, opts) } // Handle maps if v.Kind() == reflect.Map { return convertMap(v, opts) } return v.Interface(), nil } // convertSlice converts a slice/array field. func convertSlice(v reflect.Value, opts *Options) (any, error) { sliceLen := v.Len() result := make([]any, sliceLen) for i := 0; i < sliceLen; i++ { elem := v.Index(i) val, err := convertField(elem, reflect.StructField{}, opts) if err != nil { return nil, err } result[i] = val } return result, nil } // convertMap converts a map field. func convertMap(v reflect.Value, opts *Options) (any, error) { result := make(map[string]any) iter := v.MapRange() for iter.Next() { key := iter.Key() val := iter.Value() keyStr, ok := key.Interface().(string) if !ok { return nil, fmt.Errorf("map key must be string, got %v", key.Kind()) } value, err := convertField(val, reflect.StructField{}, opts) if err != nil { return nil, err } result[keyStr] = value } return result, nil }