summaryrefslogtreecommitdiff
path: root/pkg/server/opts/opts.go
diff options
context:
space:
mode:
authorMikhail Osipov <mike.osipov@gmail.com>2020-12-16 15:27:48 +0300
committerMikhail Osipov <mike.osipov@gmail.com>2020-12-16 15:27:48 +0300
commit6fed9dd0dd62718f78eca11e30a71c2712636fbd (patch)
tree8d1f90b96efbe8ea8aea350c283325adc216ef9d /pkg/server/opts/opts.go
parent050ea053dd549f0dd01beddfcd74989858391fd7 (diff)
hook and socket args check fix, tests
Diffstat (limited to 'pkg/server/opts/opts.go')
-rw-r--r--pkg/server/opts/opts.go149
1 files changed, 146 insertions, 3 deletions
diff --git a/pkg/server/opts/opts.go b/pkg/server/opts/opts.go
index 22383d8..c5729a3 100644
--- a/pkg/server/opts/opts.go
+++ b/pkg/server/opts/opts.go
@@ -1,6 +1,19 @@
package opts
-import "strings"
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+type fieldSpec struct {
+ name string
+ inline bool
+ required bool
+ positive bool
+ defaultValue string
+}
type Opts map[string]string
@@ -20,7 +33,137 @@ func Parse(s string) (string, Opts) {
return v[0], m
}
-func (m Opts) Bool(key string) bool {
- _, ok := m[key]
+func (opts Opts) Bool(key string) bool {
+ _, ok := opts[key]
return ok
}
+
+func (opts Opts) Configure(v interface{}) error {
+ rv := reflect.ValueOf(v)
+
+ 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())
+ }
+
+ return opts.configure(rv.Elem())
+}
+
+func getFieldSpec(field reflect.StructField) (fs fieldSpec) {
+ fs.name = strings.ToLower(field.Name)
+
+ for _, s := range strings.Split(field.Tag.Get("opts"), ",") {
+ switch {
+ case s == "inline":
+ fs.inline = true
+ case s == "required":
+ fs.required = true
+ case s == "positive":
+ fs.positive = true
+ case strings.HasPrefix(s, "default:"):
+ fs.defaultValue = s[8:]
+ }
+ }
+
+ return
+}
+
+func (opts Opts) setValue(v reflect.Value, fs fieldSpec) error {
+ s, ok := opts[fs.name]
+ kind := v.Kind()
+
+ if kind == reflect.Bool {
+ if s != "" {
+ return fmt.Errorf("%s: boolean option does not need a value", fs.name)
+ }
+
+ if ok {
+ v.SetBool(true)
+ }
+
+ return nil
+ }
+
+ if s == "" {
+ if fs.required {
+ return fmt.Errorf("%s: expect value", fs.name)
+ }
+
+ s = fs.defaultValue
+ }
+
+ if s == "" {
+ return nil
+ }
+
+ switch kind {
+ case reflect.String:
+ v.SetString(s)
+ case reflect.Int:
+ n, err := strconv.Atoi(s)
+ if err != nil {
+ return fmt.Errorf("%s: %w", fs.name, err)
+ }
+ if n <= 0 {
+ return fmt.Errorf("%s: expect positive", fs.name)
+ }
+ v.SetInt(int64(n))
+ default:
+ return fmt.Errorf("%s: unsupported type '%s'", fs.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
+ }
+
+ var visit func(v reflect.Value) error
+
+ visit = func(v reflect.Value) error {
+ t := v.Type()
+
+ for n := 0; n < v.NumField(); n++ {
+ fs := getFieldSpec(t.Field(n))
+ fv := v.Field(n)
+
+ 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
+ }
+
+ delete(m, fs.name)
+ }
+ }
+
+ return nil
+ }
+
+ if err := visit(v); err != nil {
+ return err
+ }
+
+ if len(m) > 0 {
+ var ss []string
+
+ for s := range m {
+ ss = append(ss, s)
+ }
+
+ return fmt.Errorf("unknown options: %s", strings.Join(ss, ","))
+ }
+
+ return nil
+}