package cfg import ( "fmt" "reflect" "strconv" "strings" "tuxpa.in/t/wm/src/copies" ) // Read will return the first non nil value or the default value func Read[T any](xs ...*T) T { for _, v := range xs { if v != nil { return *v } } // zero value if all else fails return *new(T) } func ReadFunc[T any, V any](fn func(*T) *V, xs ...*T) V { for _, v := range xs { if v == nil { continue } vv := fn(v) if vv != nil { return *vv } } return *new(V) } type Modifier[T any] struct { Ref T setters map[string]func(v string) error getters map[string]func() (string, error) } 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 } 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) } 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)) }