package osu import ( "encoding/json" "fmt" "reflect" "dario.cat/mergo" "github.com/qsfera/server/pkg/conversions" ) type Builder interface { json.Marshaler Map() (map[string]any, error) } func newBase(v ...any) (map[string]any, error) { base := make(map[string]any) for _, value := range v { data, err := conversions.To[map[string]any](value) if err != nil { return nil, fmt.Errorf("failed to convert value to map: %w", err) } if isEmpty(data) { continue } if err := mergo.Merge(&base, data); err != nil { return nil, fmt.Errorf("failed to merge value into base: %w", err) } } return base, nil } func applyValue[T any](target map[string]any, key string, v T) { if target == nil || isEmpty(key) || isEmpty(v) { return } target[key] = v } func applyValues[T any](target map[string]any, values map[string]T) { if target == nil || isEmpty(values) { return } for k, v := range values { applyValue[T](target, k, v) } } func applyBuilder(target map[string]any, key string, builder Builder) error { if target == nil || isEmpty(key) || isEmpty(builder) { return nil } data, err := builder.Map() if err != nil { return fmt.Errorf("failed to map builder %s: %w", key, err) } if !isEmpty(data) { target[key] = data } return nil } func applyBuilders(target map[string]any, key string, bs ...Builder) error { if target == nil || isEmpty(key) || isEmpty(bs) { return nil } builders := make([]map[string]any, 0, len(bs)) for _, builder := range bs { data, err := builder.Map() switch { case err != nil: return fmt.Errorf("failed to map builder %s: %w", key, err) case isEmpty(data): continue default: builders = append(builders, data) } } if len(builders) > 0 { target[key] = builders } return nil } func isEmpty(x any) bool { switch { case x == nil: return true case reflect.ValueOf(x).Kind() == reflect.Bool: return false case reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface()): return true case reflect.ValueOf(x).Kind() == reflect.Map && reflect.ValueOf(x).Len() == 0: return true default: return false } } func merge[T any](vals ...T) T { base := make(map[string]any) for _, val := range vals { data, err := conversions.To[map[string]any](val) if err != nil { continue } _ = mergo.Merge(&base, data) } data, _ := conversions.To[T](base) return data }