129 lines
2.4 KiB
Go
129 lines
2.4 KiB
Go
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
|
|
}
|