summaryrefslogtreecommitdiff
path: root/pkg/server/opts/opts.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/server/opts/opts.go')
-rw-r--r--pkg/server/opts/opts.go168
1 files changed, 107 insertions, 61 deletions
diff --git a/pkg/server/opts/opts.go b/pkg/server/opts/opts.go
index c5729a3..2c781e3 100644
--- a/pkg/server/opts/opts.go
+++ b/pkg/server/opts/opts.go
@@ -7,12 +7,24 @@ import (
"strings"
)
-type fieldSpec struct {
- name string
- inline bool
- required bool
- positive bool
- defaultValue string
+type Kind = reflect.Kind
+
+const (
+ Bool = reflect.Bool
+ Int = reflect.Int
+ String = reflect.String
+)
+
+type Param struct {
+ Name string
+ Kind Kind
+
+ Default string
+ Required bool
+
+ Positive bool
+
+ index []int
}
type Opts map[string]string
@@ -38,47 +50,55 @@ func (opts Opts) Bool(key string) bool {
return ok
}
-func (opts Opts) Configure(v interface{}) error {
- rv := reflect.ValueOf(v)
+func extract(i interface{}) (reflect.Value, error) {
+ v := reflect.ValueOf(i)
+
+ var err error
switch {
- case rv.Kind() != reflect.Ptr:
- return fmt.Errorf("opts: configure failed: non-pointer %s", rv.Type().String())
- case rv.IsNil():
- return fmt.Errorf("opts: configure failed: nil %s", rv.Type().String())
- case rv.Elem().Kind() != reflect.Struct:
- return fmt.Errorf("opts: configure failed: non-struct %s", rv.Elem().Type().String())
+ case v.Kind() != reflect.Ptr:
+ err = fmt.Errorf("non-pointer %s", v.Type().String())
+ case v.IsNil():
+ err = fmt.Errorf("nil %s", v.Type().String())
+ case v.Elem().Kind() != reflect.Struct:
+ err = fmt.Errorf("non-struct %s", v.Elem().Type().String())
+ }
+
+ if err != nil {
+ return reflect.Value{}, err
}
- return opts.configure(rv.Elem())
+ return v.Elem(), nil
}
-func getFieldSpec(field reflect.StructField) (fs fieldSpec) {
- fs.name = strings.ToLower(field.Name)
+func (opts Opts) Configure(i interface{}) error {
+ v, err := extract(i)
+ if err != nil {
+ return fmt.Errorf("opts: configure failed: %w", err)
+ }
- for _, s := range strings.Split(field.Tag.Get("opts"), ",") {
+ return opts.configure(v)
+}
+
+func (p *Param) parseTags(tags string) {
+ for _, s := range strings.Split(tags, ",") {
switch {
- case s == "inline":
- fs.inline = true
case s == "required":
- fs.required = true
+ p.Required = true
case s == "positive":
- fs.positive = true
+ p.Positive = true
case strings.HasPrefix(s, "default:"):
- fs.defaultValue = s[8:]
+ p.Default = s[8:]
}
}
-
- return
}
-func (opts Opts) setValue(v reflect.Value, fs fieldSpec) error {
- s, ok := opts[fs.name]
- kind := v.Kind()
+func (opts Opts) setValue(v reflect.Value, p Param) error {
+ s, ok := opts[p.Name]
- if kind == reflect.Bool {
+ if p.Kind == Bool {
if s != "" {
- return fmt.Errorf("%s: boolean option does not need a value", fs.name)
+ return fmt.Errorf("%s: boolean option does not need a value", p.Name)
}
if ok {
@@ -89,70 +109,96 @@ func (opts Opts) setValue(v reflect.Value, fs fieldSpec) error {
}
if s == "" {
- if fs.required {
- return fmt.Errorf("%s: expect value", fs.name)
+ if p.Required {
+ return fmt.Errorf("%s: expect value", p.Name)
}
- s = fs.defaultValue
+ s = p.Default
}
if s == "" {
return nil
}
- switch kind {
- case reflect.String:
+ switch p.Kind {
+ case String:
v.SetString(s)
- case reflect.Int:
+ case Int:
n, err := strconv.Atoi(s)
if err != nil {
- return fmt.Errorf("%s: %w", fs.name, err)
+ return fmt.Errorf("%s: %w", p.Name, err)
}
if n <= 0 {
- return fmt.Errorf("%s: expect positive", fs.name)
+ return fmt.Errorf("%s: expect positive", p.Name)
}
v.SetInt(int64(n))
default:
- return fmt.Errorf("%s: unsupported type '%s'", fs.name, v.Type().String())
+ return fmt.Errorf("%s: unsupported type '%s'", p.Name, v.Type().String())
}
return nil
}
-func (opts Opts) configure(v reflect.Value) error {
- m := map[string]bool{}
-
- for s := range opts {
- m[s] = true
+func Parametrize(t reflect.Type) []Param {
+ if t.Kind() != reflect.Struct {
+ return nil
}
- var visit func(v reflect.Value) error
+ return parametrize(reflect.New(t).Elem())
+}
- visit = func(v reflect.Value) error {
- t := v.Type()
+func parametrize(v reflect.Value) (param []Param) {
+ var visit func(index []int, v reflect.Value)
+ visit = func(index []int, v reflect.Value) {
for n := 0; n < v.NumField(); n++ {
- fs := getFieldSpec(t.Field(n))
- fv := v.Field(n)
+ f := v.Type().Field(n)
+ x := append(append([]int{}, index...), f.Index...)
- if fs.inline {
- if err := visit(fv); err != nil {
- return err
- }
- } else if fv.CanSet() {
- if err := opts.setValue(v.Field(n), fs); err != nil {
- return err
- }
+ if f.Anonymous {
+ visit(x, v.Field(n))
+ continue
+ }
+
+ if !v.Field(n).CanSet() {
+ continue
+ }
- delete(m, fs.name)
+ p := Param{
+ Name: strings.ToLower(f.Name),
+ Kind: f.Type.Kind(),
+
+ index: x,
}
+
+ p.parseTags(f.Tag.Get("opts"))
+
+ param = append(param, p)
}
+ }
- return nil
+ visit(nil, v)
+
+ return
+}
+
+func (opts Opts) configure(v reflect.Value) error {
+ param := parametrize(v)
+
+ m := map[string]bool{}
+
+ for s := range opts {
+ m[s] = true
}
- if err := visit(v); err != nil {
- return err
+ for _, p := range param {
+ f := v.FieldByIndex(p.index)
+
+ if err := opts.setValue(f, p); err != nil {
+ return err
+ }
+
+ delete(m, p.Name)
}
if len(m) > 0 {