diff --git a/flag.go b/flag.go index 1fa62a2..2cb4d77 100644 --- a/flag.go +++ b/flag.go @@ -5,17 +5,26 @@ import ( "errors" "flag" "reflect" - "strconv" + "time" "github.com/unistack-org/micro/v3/config" rutil "github.com/unistack-org/micro/v3/util/reflect" ) var ( - DefaultStructTag = "flag" - ErrInvalidStruct = errors.New("invalid struct specified") + DefaultStructTag = "flag" + ErrInvalidValue = errors.New("invalid value specified") + DefaultSliceDelim = "," + DefaultMapDelim = "," ) +/* +var ( + timeTimeKind = reflect.TypeOf(time.Time{}).Kind() + timeDurationKind = reflect.TypeOf(time.Duration(0)).Kind() +) +*/ + type flagConfig struct { opts config.Options } @@ -41,15 +50,51 @@ func (c *flagConfig) Init(opts ...config.Option) error { } fn, fv, fd := getFlagOpts(tf) + rcheck := true + switch sf.Value.Interface().(type) { + case time.Duration: + err = c.flagDuration(sf.Value, fn, fv, fd) + rcheck = false + case time.Time: + err = c.flagTime(sf.Value, fn, fv, fd) + rcheck = false + } + if err != nil { + return err + } + + if !rcheck { + continue + } + + if sf.Value.Kind() == reflect.Ptr { + sf.Value = sf.Value.Elem() + } + switch sf.Value.Kind() { case reflect.String: - v := sf.Value.Addr().Interface().(*string) - flag.StringVar(v, fn, fv, fd) + err = c.flagString(sf.Value, fn, fv, fd) case reflect.Bool: - v := sf.Value.Addr().Interface().(*bool) - i, _ := strconv.ParseBool(fv) - flag.BoolVar(v, fn, i, fd) + err = c.flagBool(sf.Value, fn, fv, fd) + case reflect.Int: + err = c.flagInt(sf.Value, fn, fv, fd) + case reflect.Int64: + err = c.flagInt64(sf.Value, fn, fv, fd) + case reflect.Float64: + err = c.flagFloat64(sf.Value, fn, fv, fd) + case reflect.Uint: + err = c.flagUint(sf.Value, fn, fv, fd) + case reflect.Uint64: + err = c.flagUint64(sf.Value, fn, fv, fd) + case reflect.Slice: + err = c.flagSlice(sf.Value, fn, fv, fd) + case reflect.Map: + err = c.flagMap(sf.Value, fn, fv, fd) } + if err != nil { + return err + } + } return nil diff --git a/flag_test.go b/flag_test.go index 20c611b..6f31fb9 100644 --- a/flag_test.go +++ b/flag_test.go @@ -1,49 +1,46 @@ package flag import ( - "flag" + "context" "os" - "reflect" - "strconv" "testing" + "time" - rutil "github.com/unistack-org/micro/v3/util/reflect" + "github.com/unistack-org/micro/v3/config" ) func TestLoad(t *testing.T) { os.Args = append(os.Args, "-broker", "5566:33") - type config struct { - Broker string `flag:"name=broker,desc='description with, comma',default='127.0.0.1:9092'"` - Verbose bool `flag:"name=verbose,desc='verbose output',default='false value'"` + os.Args = append(os.Args, "-verbose") + os.Args = append(os.Args, "-wait", "5s") + os.Args = append(os.Args, "-addr", "33,44") + os.Args = append(os.Args, "-time", time.RFC822) + type Config struct { + Broker string `flag:"name=broker,desc='description with, comma',default='127.0.0.1:9092'"` + Verbose bool `flag:"name=verbose,desc='verbose output',default='false'"` + Addr []string `flag:"name=addr,desc='addrs',default='127.0.0.1:9092'"` + Wait time.Duration `flag:"name=wait,desc='wait time',default='2s'"` + Time time.Time `flag:"name=time,desc='some time',default='02 Jan 06 15:04 MST'"` } - cfg := &config{} + ctx := context.Background() + cfg := &Config{} - fields, err := rutil.StructFields(cfg) - if err != nil { + c := NewConfig(config.Struct(cfg), TimeFormat(time.RFC822)) + if err := c.Init(); err != nil { t.Fatal(err) } - for _, sf := range fields { - tf, ok := sf.Field.Tag.Lookup("flag") - if !ok { - continue - } - fn, fv, fd := getFlagOpts(tf) - - switch sf.Value.Kind() { - case reflect.String: - v := sf.Value.Addr().Interface().(*string) - flag.StringVar(v, fn, fv, fd) - case reflect.Bool: - v := sf.Value.Addr().Interface().(*bool) - i, _ := strconv.ParseBool(fv) - flag.BoolVar(v, fn, i, fd) - } + if err := c.Load(ctx); err != nil { + t.Fatal(err) } - flag.Parse() if cfg.Broker != "5566:33" { t.Fatalf("failed to parse flags broker value invalid: %#+v", cfg) } + if tf := cfg.Time.Format(time.RFC822); tf != "02 Jan 06 14:32 MSK" { + t.Fatalf("parse time error: %v", cfg.Time) + } + + t.Logf("cfg %#+v", cfg) } diff --git a/go.mod b/go.mod index 4b9944b..f56cadf 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/unistack-org/micro-config-flag/v3 go 1.16 -require github.com/unistack-org/micro/v3 v3.3.22 +require github.com/unistack-org/micro/v3 v3.3.23 diff --git a/go.sum b/go.sum index 21cf3a2..0669248 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,8 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/silas/dag v0.0.0-20210121180416-41cf55125c34/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I= -github.com/unistack-org/micro/v3 v3.3.22 h1:4mY+lhqW6N/3A0LSfAnGHE65TakGOrbU144/FQEjZIs= -github.com/unistack-org/micro/v3 v3.3.22/go.mod h1:LXmPfbJnJNvL0kQs8HfnkV3Wya2Wb+C7keVq++RCZnk= +github.com/unistack-org/micro/v3 v3.3.23 h1:iQtvVF4p+HPtWgm/zPt7+gN78EQMf1rHSMppxYlbRHQ= +github.com/unistack-org/micro/v3 v3.3.23/go.mod h1:LXmPfbJnJNvL0kQs8HfnkV3Wya2Wb+C7keVq++RCZnk= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/options.go b/options.go new file mode 100644 index 0000000..e584811 --- /dev/null +++ b/options.go @@ -0,0 +1,26 @@ +package flag + +import ( + "github.com/unistack-org/micro/v3/config" +) + +type sliceDelimKey struct{} + +// SliceDelim set the slice delimeter +func SliceDelim(s string) config.Option { + return config.SetOption(sliceDelimKey{}, s) +} + +type mapDelimKey struct{} + +// MapDelim set the map delimeter +func MapDelim(s string) config.Option { + return config.SetOption(mapDelimKey{}, s) +} + +type timeFormatKey struct{} + +// TimeFormat set the time format +func TimeFormat(s string) config.Option { + return config.SetOption(timeFormatKey{}, s) +} diff --git a/util.go b/util.go index 0777ac0..556d1c6 100644 --- a/util.go +++ b/util.go @@ -1,9 +1,203 @@ package flag import ( + "flag" + "reflect" + "strconv" "strings" + "time" ) +func (c *flagConfig) flagSlice(v reflect.Value, fn, fv, fd string) error { + delim := DefaultSliceDelim + if c.opts.Context != nil { + if d, ok := c.opts.Context.Value(sliceDelimKey{}).(string); ok { + delim = d + } + } + + flag.Func(fn, fd, func(s string) error { + p := strings.Split(s, delim) + v.Set(reflect.MakeSlice(v.Type(), len(p), len(p))) + switch v.Type().Elem().Kind() { + case reflect.Int, reflect.Int64: + for idx := range p { + i, err := strconv.ParseInt(p[idx], 10, 64) + if err != nil { + return err + } + v.Index(idx).SetInt(i) + } + case reflect.Uint, reflect.Uint64: + for idx := range p { + i, err := strconv.ParseUint(p[idx], 10, 64) + if err != nil { + return err + } + v.Index(idx).SetUint(i) + } + case reflect.Float64: + for idx := range p { + i, err := strconv.ParseFloat(p[idx], 64) + if err != nil { + return err + } + v.Index(idx).SetFloat(i) + } + case reflect.Bool: + for idx := range p { + i, err := strconv.ParseBool(p[idx]) + if err != nil { + return err + } + v.Index(idx).SetBool(i) + } + case reflect.String: + for idx := range p { + v.Index(idx).SetString(p[idx]) + } + } + return nil + }) + + return nil +} + +func (c *flagConfig) flagMap(v reflect.Value, fn, fv, fd string) error { + return nil +} + +func (c *flagConfig) flagTime(v reflect.Value, fn, fv, fd string) error { + var format string + if c.opts.Context != nil { + if tf, ok := c.opts.Context.Value(timeFormatKey{}).(string); ok { + format = tf + } + } + if format == "" { + return ErrInvalidValue + } + flag.Func(fn, fd, func(s string) error { + t, err := time.Parse(s, format) + if err != nil { + return err + } + v.Set(reflect.ValueOf(t)) + return nil + }) + + return nil +} + +func (c *flagConfig) flagDuration(v reflect.Value, fn, fv, fd string) error { + nv, ok := v.Addr().Interface().(*time.Duration) + if !ok { + return ErrInvalidValue + } + i, err := time.ParseDuration(fd) + if err != nil { + return err + } + flag.DurationVar(nv, fn, i, fd) + return nil +} + +func (c *flagConfig) flagBool(v reflect.Value, fn, fv, fd string) error { + nv, ok := v.Addr().Interface().(*bool) + if !ok { + return ErrInvalidValue + } + i, err := strconv.ParseBool(fd) + if err != nil { + return err + } + flag.BoolVar(nv, fn, i, fd) + return nil +} + +func (c *flagConfig) flagString(v reflect.Value, fn, fv, fd string) error { + nv, ok := v.Addr().Interface().(*string) + if !ok { + return ErrInvalidValue + } + flag.StringVar(nv, fn, fv, fd) + return nil +} + +func (c *flagConfig) flagInt(v reflect.Value, fn, fv, fd string) error { + nv, ok := v.Addr().Interface().(*int) + if !ok { + return ErrInvalidValue + } + i, err := strconv.ParseInt(fd, 10, 64) + if err != nil { + return err + } + flag.IntVar(nv, fn, int(i), fd) + return nil +} + +func (c *flagConfig) flagInt64(v reflect.Value, fn, fv, fd string) error { + nv, ok := v.Addr().Interface().(*int64) + if !ok { + return ErrInvalidValue + } + i, err := strconv.ParseInt(fd, 10, 64) + if err != nil { + return err + } + flag.Int64Var(nv, fn, int64(i), fd) + return nil +} + +func (c *flagConfig) flagUint(v reflect.Value, fn, fv, fd string) error { + nv, ok := v.Addr().Interface().(*uint) + if !ok { + return ErrInvalidValue + } + i, err := strconv.ParseUint(fd, 10, 64) + if err != nil { + return err + } + flag.UintVar(nv, fn, uint(i), fd) + return nil +} + +func (c *flagConfig) flagUint64(v reflect.Value, fn, fv, fd string) error { + nv, ok := v.Addr().Interface().(*uint64) + if !ok { + return ErrInvalidValue + } + i, err := strconv.ParseUint(fd, 10, 64) + if err != nil { + return err + } + flag.Uint64Var(nv, fn, uint64(i), fd) + return nil +} + +func (c *flagConfig) flagFloat64(v reflect.Value, fn, fv, fd string) error { + nv, ok := v.Addr().Interface().(*float64) + if !ok { + return ErrInvalidValue + } + i, err := strconv.ParseFloat(fd, 64) + if err != nil { + return err + } + flag.Float64Var(nv, fn, float64(i), fd) + return nil +} + +func (c *flagConfig) flagStringSlice(v reflect.Value, fn, fv, fd string) error { + nv, ok := v.Addr().Interface().(*string) + if !ok { + return ErrInvalidValue + } + flag.StringVar(nv, fn, fv, fd) + return nil +} + func getFlagOpts(tf string) (string, string, string) { ret := make([]string, 3) vals := strings.Split(tf, ",")