137 lines
2.7 KiB
Go
137 lines
2.7 KiB
Go
package settings
|
|
|
|
import (
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
type (
|
|
// @todo support Decoder interface
|
|
// Decoder interface {
|
|
// Decode(kv KV, prefix string) error
|
|
// }
|
|
)
|
|
|
|
var (
|
|
// @todo support Decoder interface
|
|
// decoderTyEl = reflect.TypeOf((*Decoder)(nil)).Elem()
|
|
)
|
|
|
|
// Decode converts key-value (KV) into structs using tags & field names
|
|
//
|
|
// Supports decoding into all scalar types, can handle nested structures and simple maps (1 dim, string as key)
|
|
//
|
|
// Example:
|
|
// key-value pairs:
|
|
// string1: "string"
|
|
// number: 42
|
|
//
|
|
// struct{
|
|
// String1 string `kv:"string1`
|
|
// Number int
|
|
// }
|
|
//
|
|
func Decode(kv KV, dst interface{}, pp ...string) (err error) {
|
|
v := reflect.ValueOf(dst)
|
|
if v.Kind() != reflect.Ptr {
|
|
return errors.New("expecting a pointer, not a value")
|
|
}
|
|
|
|
if v.IsNil() {
|
|
return errors.New("nil pointer passed")
|
|
}
|
|
|
|
v = v.Elem()
|
|
|
|
var prefix string
|
|
if len(pp) > 0 {
|
|
// If called with prefix, join string slice + 1 empty string (to ensure tailing dot)
|
|
prefix = strings.Join(append(pp, ""), ".")
|
|
}
|
|
|
|
length := v.NumField()
|
|
|
|
for i := 0; i < length; i++ {
|
|
var (
|
|
f = v.Field(i)
|
|
t = v.Type().Field(i)
|
|
|
|
key = prefix + strings.ToLower(t.Name[:1]) + t.Name[1:]
|
|
tag = t.Tag.Get("kv")
|
|
|
|
tagFlags []string
|
|
)
|
|
|
|
if tag == "-" {
|
|
// Skip fields with kv tags equal to "-"
|
|
continue
|
|
}
|
|
|
|
// if !f.CanSet() {
|
|
// return errors.New("unexpected pointer for field " + t.Name)
|
|
// }
|
|
|
|
if tag != "" {
|
|
tagFlags = strings.Split(tag, ",")
|
|
|
|
if tagFlags[0] != "" {
|
|
// key explicitly set via tag, use that!
|
|
key = prefix + tagFlags[0]
|
|
}
|
|
|
|
for f := 1; f < len(tagFlags); f++ {
|
|
// @todo resolve i18n and other flags...
|
|
}
|
|
}
|
|
|
|
// @todo handle Decoder interface
|
|
// if f.Type().Implements(decoderTyEl) {
|
|
// result := reflect.ValueOf(&t).MethodByName("Decode").Call([]reflect.Value{
|
|
// reflect.ValueOf(kv.Filter(key)),
|
|
// reflect.ValueOf(prefix),
|
|
// })
|
|
//
|
|
// if len(result) != 1 {
|
|
// return errors.New("internal error, Decoder signature does not match")
|
|
// }
|
|
// }
|
|
|
|
// Handles structs
|
|
//
|
|
// It calls Decode recursively
|
|
if f.Kind() == reflect.Struct {
|
|
if err = Decode(kv.Filter(key), f.Addr().Interface(), key); err != nil {
|
|
return
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
// Handles map values
|
|
if f.Kind() == reflect.Map {
|
|
if f.IsNil() {
|
|
// allocate new map
|
|
f.Set(reflect.MakeMap(f.Type()))
|
|
}
|
|
|
|
for k, val := range kv.CutPrefix(key + ".") {
|
|
mapValue := reflect.New(f.Type().Elem())
|
|
val.Unmarshal(mapValue.Interface())
|
|
f.SetMapIndex(reflect.ValueOf(k), mapValue.Elem())
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
if val, ok := kv[key]; ok {
|
|
if err = val.Unmarshal(f.Addr().Interface()); err != nil {
|
|
return
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|