From f8ba70c1a75eae43f4f60e962e49e3c6688bd0df Mon Sep 17 00:00:00 2001 From: xgfone Date: Thu, 10 Oct 2019 21:51:28 +0800 Subject: [PATCH] add the Fix field for Opt --- config_test.go | 24 ++++++++++++++++++++++++ group.go | 17 +++++++++++++++-- opt.go | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/config_test.go b/config_test.go index 3457e3c..27b2dd0 100644 --- a/config_test.go +++ b/config_test.go @@ -20,6 +20,30 @@ import ( "testing" ) +func ExampleOpt_F() { + fix := func(v interface{}) (interface{}, error) { return v.(int) + 1, nil } + opt1 := IntOpt("opt1", "test fix with default").D(10).F(fix, true) + opt2 := IntOpt("opt2", "test fix without default").D(20).F(fix) + + conf := New() + conf.RegisterOpts([]Opt{opt1, opt2}) + + fmt.Printf("opt1=%s\n", conf.MustString("opt1")) + fmt.Printf("opt2=%s\n", conf.MustString("opt2")) + + conf.UpdateValue("opt1", 30) + conf.UpdateValue("opt2", 40) + + fmt.Printf("opt1=%s\n", conf.MustString("opt1")) + fmt.Printf("opt2=%s\n", conf.MustString("opt2")) + + // Output: + // opt1=11 + // opt2=20 + // opt1=31 + // opt2=41 +} + func TestOptObserver(t *testing.T) { var value string opt := StrOpt("opt", "").D("abc").O(func(v interface{}) { value = v.(string) }) diff --git a/group.go b/group.go index 5a22ca6..9f32a88 100644 --- a/group.go +++ b/group.go @@ -331,7 +331,9 @@ func (g *OptGroup) setOptWatch(name string, watch func(interface{})) { func (g *OptGroup) registerOpt(opt Opt, force ...bool) (ok bool) { opt.check() - if err := opt.validate(opt.Default); err != nil { + if err := opt.fix(); err != nil { + panic(NewOptError(g.name, opt.Name, err, opt.Default)) + } else if err := opt.validate(opt.Default); err != nil { panic(NewOptError(g.name, opt.Name, err, opt.Default)) } @@ -361,7 +363,9 @@ func (g *OptGroup) registerOpts(opts []Opt, force ...bool) (ok bool) { names := make([]string, len(opts)) for i := range opts { opts[i].check() - if err := opts[i].validate(opts[i].Default); err != nil { + if err := opts[i].fix(); err != nil { + panic(NewOptError(g.name, opts[i].Name, err, opts[i].Default)) + } else if err := opts[i].validate(opts[i].Default); err != nil { panic(NewOptError(g.name, opts[i].Name, err, opts[i].Default)) } names[i] = g.fixOptName(opts[i].Name) @@ -535,6 +539,15 @@ func (g *OptGroup) parseOptValue(name string, value interface{}) (interface{}, e return nil, NewOptError(g.name, opt.opt.Name, err, value) } + // Fix the parsed value + if opt.opt.Fix != nil { + _v, err := opt.opt.Fix(v) + if err != nil { + return nil, NewOptError(g.name, opt.opt.Name, err, v) + } + v = _v + } + // Validate the option value if err = opt.opt.validate(v); err != nil { return nil, NewOptError(g.name, opt.opt.Name, err, v) diff --git a/opt.go b/opt.go index 2c5b7fe..cc4b865 100644 --- a/opt.go +++ b/opt.go @@ -56,6 +56,17 @@ type Opt struct { // Notice: it must not panic. Parser func(input interface{}) (output interface{}, err error) + // Fix is used to fix the parsed value. + // + // The different between Parser and Fix: + // 1. Parser only parses the value from the arbitrary type to a specific. + // 2. Fix only changes the value, not the type, that's, input and output + // should be the same type. For example, input is the NIC name, + // and Fix can get the ip by the NIC name then return it as output. + // So it ensures that input may be NIC or IP, and that the value + // of the option is always a IP. + Fix func(input interface{}) (output interface{}, err error) + // Observers are called after the value of the option is updated. Observers []func(newValue interface{}) @@ -67,6 +78,8 @@ type Opt struct { // // Notice: they must not panic. Validators []Validator + + fixDefault bool } func (o Opt) check() { @@ -118,6 +131,30 @@ func (o Opt) D(_default interface{}) Opt { return o } +func (o *Opt) fix() error { + if o.fixDefault && o.Fix != nil && o.Default != nil { + _default, err := o.Fix(o.Default) + if err != nil { + return err + } + o.Default = _default + } + return nil +} + +// F returns a new Opt with the given fix function based on the current option. +// +// If fixDefault is true, it will fix the default value when registering +// the option. +func (o Opt) F(fix func(interface{}) (interface{}, error), fixDefault ...bool) Opt { + o.Fix = fix + if len(fixDefault) > 0 { + o.fixDefault = fixDefault[0] + } + + return o +} + // H returns a new Opt with the given help based on the current option. func (o Opt) H(help string) Opt { o.Help = help