wm/src/bsp/cfg/cfg.go
2023-06-11 09:26:55 -05:00

161 lines
3.3 KiB
Go

package cfg
import (
"fmt"
"reflect"
"strconv"
"strings"
"sync"
"tuxpa.in/t/wm/src/copies"
)
type Modifier[T any] struct {
Ref T
setters map[string]func(v string) error
getters map[string]func() (string, error)
mu sync.RWMutex
}
func NewModifier[T any](start T) *Modifier[T] {
m := &Modifier[T]{
Ref: start,
setters: map[string]func(v string) error{},
getters: map[string]func() (string, error){},
}
m.setup()
return m
}
func (m *Modifier[T]) Set(k, v string) error {
fn, ok := m.setters[k]
if !ok {
// TODO: some error here
return nil
}
m.mu.Lock()
defer m.mu.Unlock()
return fn(v)
}
func (m *Modifier[T]) GetString(k string) (string, error) {
fn, ok := m.getters[k]
if !ok {
// TODO: some error here
return "", fmt.Errorf("config key '%s' not found", k)
}
m.mu.RLock()
defer m.mu.RUnlock()
return fn()
}
func (m *Modifier[T]) FillDefaults() {
rt := reflect.TypeOf(m.Ref).Elem()
for i := 0; i < rt.NumField(); i++ {
ft := rt.Field(i)
k := ft.Tag.Get("cfg")
dv := ft.Tag.Get("default")
m.Set(k, dv)
}
return
}
func (m *Modifier[T]) setup() {
// grab the value
rv := reflect.ValueOf(m.Ref).Elem()
rt := reflect.TypeOf(m.Ref).Elem()
for i := 0; i < rv.NumField(); i++ {
fv := rv.Field(i)
ft := rt.Field(i)
k := ft.Tag.Get("cfg")
kind := ft.Type.Elem().Kind()
switch kind {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
m.setters[k] = func(v string) (err error) {
var val uint64
if v != "" {
val, err = strconv.ParseUint(v, 10, 64)
if err != nil {
return copies.NewInvalidValueErr(k, v)
}
}
if fv.IsNil() {
fv.Set(reflect.New(ft.Type.Elem()))
}
fv.Elem().SetUint(uint64(val))
return nil
}
m.getters[k] = func() (string, error) {
if fv.IsNil() {
return "", nil
}
return fmt.Sprintf("%v", fv.Elem()), nil
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
m.setters[k] = func(v string) (err error) {
var val int
if v != "" {
val, err = strconv.Atoi(v)
if err != nil {
return copies.NewInvalidValueErr(k, v)
}
}
if fv.IsNil() {
fv.Set(reflect.New(ft.Type.Elem()))
}
fv.Elem().SetInt(int64(val))
return nil
}
m.getters[k] = func() (string, error) {
if fv.IsNil() {
return "", nil
}
return fmt.Sprintf("%v", fv.Elem()), nil
}
case reflect.Bool:
m.setters[k] = func(v string) error {
var b bool
switch strings.ToLower(v) {
case "true", "on":
b = true
case "false", "off", "":
b = false
default:
return copies.NewInvalidValueErr(k, v)
}
if fv.IsNil() {
fv.Set(reflect.New(ft.Type.Elem()))
}
fv.Elem().SetBool(b)
return nil
}
m.getters[k] = func() (string, error) {
if fv.IsNil() {
return "", nil
}
return fmt.Sprintf("%v", fv.Elem()), nil
}
case reflect.String:
m.setters[k] = func(v string) error {
if fv.IsNil() {
fv.Set(reflect.New(ft.Type.Elem()))
}
fv.Elem().SetString(strings.TrimSpace(v))
return nil
}
m.getters[k] = func() (string, error) {
if fv.IsNil() {
return "", nil
}
return fmt.Sprintf("%v", fv.Elem()), nil
}
}
}
}
func getStructTag(f reflect.StructField, tagName string) string {
return string(f.Tag.Get(tagName))
}