Skip to content

Commit

Permalink
add the Fix field for Opt
Browse files Browse the repository at this point in the history
  • Loading branch information
xgfone committed Oct 10, 2019
1 parent 40b6b4f commit f8ba70c
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 2 deletions.
24 changes: 24 additions & 0 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) })
Expand Down
17 changes: 15 additions & 2 deletions group.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
37 changes: 37 additions & 0 deletions opt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{})

Expand All @@ -67,6 +78,8 @@ type Opt struct {
//
// Notice: they must not panic.
Validators []Validator

fixDefault bool
}

func (o Opt) check() {
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit f8ba70c

Please sign in to comment.