Files
cunicu/pkg/config/meta.go
2022-10-07 18:30:50 +02:00

119 lines
2.0 KiB
Go

package config
import (
"reflect"
"strings"
"github.com/mitchellh/mapstructure"
)
const delim = "."
type Meta struct {
Fields map[string]*Meta
Parent *Meta
Type reflect.Type
onChanged []ChangedHandler
}
func Metadata() *Meta {
return metadata(reflect.TypeOf(Settings{}))
}
func metadata(typ reflect.Type) *Meta {
if typ.Kind() == reflect.Pointer {
typ = typ.Elem()
}
m := &Meta{
Type: typ,
}
if typ.Kind() == reflect.Struct {
m.Fields = map[string]*Meta{}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
if tag, ok := field.Tag.Lookup("koanf"); ok {
name := strings.Split(tag, ",")[0]
n := metadata(field.Type)
if name != "" {
n.Parent = m
m.Fields[name] = n
} else {
for k, v := range n.Fields {
m.Fields[k] = v
}
}
}
}
}
return m
}
func (m *Meta) Keys() []string {
keys := []string{}
for k, v := range m.Fields {
if v.Fields == nil {
keys = append(keys, k)
} else {
for _, p := range v.Keys() {
keys = append(keys, k+delim+p)
}
}
}
return keys
}
func (m *Meta) Lookup(key string) *Meta {
parts := strings.Split(key, delim)
return m.lookup(parts)
}
func (m *Meta) lookup(key []string) *Meta {
if len(key) == 0 {
return m
} else {
if m.Fields != nil {
if n, ok := m.Fields[key[0]]; ok {
return n.lookup(key[1:])
}
}
}
return nil
}
func (m *Meta) OnChanged(key string, h ChangedHandler) {
if n := m.Lookup(key); n != nil {
n.onChanged = append(n.onChanged, h)
}
}
func (m *Meta) InvokeHandlers(key string, change Change) {
for n := m.Lookup(key); n != nil; n = n.Parent {
for _, h := range n.onChanged {
h.OnConfigChanged(key, change.Old, change.New)
}
}
}
func (m *Meta) Parse(str string) (any, error) {
out := reflect.New(m.Type)
dec, err := mapstructure.NewDecoder(DecoderConfig(out.Interface()))
if err != nil {
return nil, err
}
if err := dec.Decode(str); err != nil {
return nil, err
}
return out.Elem().Interface(), nil
}