Merge pull request #42 from unistack-org/usage

using own usage func
This commit is contained in:
Василий Толстов 2022-04-02 15:22:12 +03:00 committed by GitHub
commit f1a9fa96a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 141 additions and 36 deletions

153
flag.go
View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
"strings"
"time" "time"
"go.unistack.org/micro/v3/config" "go.unistack.org/micro/v3/config"
@ -30,6 +31,8 @@ var (
type flagConfig struct { type flagConfig struct {
fset *flag.FlagSet fset *flag.FlagSet
opts config.Options opts config.Options
name string
env string
} }
func (c *flagConfig) Options() config.Options { func (c *flagConfig) Options() config.Options {
@ -40,19 +43,23 @@ func (c *flagConfig) Init(opts ...config.Option) error {
for _, o := range opts { for _, o := range opts {
o(&c.opts) o(&c.opts)
} }
c.configure()
fields, err := rutil.StructFields(c.opts.Struct) fields, err := rutil.StructFields(c.opts.Struct)
if err != nil { if err != nil {
return err return err
} }
// flag.CommandLine.Init(os.Args[0], flag.ContinueOnError)
for _, sf := range fields { for _, sf := range fields {
tf, ok := sf.Field.Tag.Lookup(c.opts.StructTag) tf, ok := sf.Field.Tag.Lookup(c.opts.StructTag)
if !ok { if !ok {
continue continue
} }
fn, fd, fv := getFlagOpts(tf) fn, fd, fv := getFlagOpts(tf)
if tf, ok = sf.Field.Tag.Lookup(c.env); ok {
fd += fmt.Sprintf(" (env %s)", tf)
}
rcheck := true rcheck := true
@ -67,7 +74,6 @@ func (c *flagConfig) Init(opts ...config.Option) error {
return nil return nil
} }
fmt.Printf("register %s flag\n", fn)
switch vi.(type) { switch vi.(type) {
case time.Duration: case time.Duration:
err = c.flagDuration(sf.Value, fn, fv, fd) err = c.flagDuration(sf.Value, fn, fv, fd)
@ -149,42 +155,125 @@ func (c *flagConfig) Watch(ctx context.Context, opts ...config.WatchOption) (con
return nil, fmt.Errorf("not implemented") return nil, fmt.Errorf("not implemented")
} }
func (c *flagConfig) usage() {
mapDelim := DefaultMapDelim
sliceDelim := DefaultSliceDelim
if c.opts.Context != nil {
if d, ok := c.opts.Context.Value(mapDelimKey{}).(string); ok {
mapDelim = d
}
if d, ok := c.opts.Context.Value(sliceDelimKey{}).(string); ok {
sliceDelim = d
}
}
if c.name == "" {
fmt.Fprintf(c.fset.Output(), "Usage:\n")
} else {
fmt.Fprintf(c.fset.Output(), "Usage of %s:\n", c.name)
}
c.fset.VisitAll(func(f *flag.Flag) {
var b strings.Builder
fmt.Fprintf(&b, " -%s", f.Name) // Two spaces before -; see next two comments.
_, usage := flag.UnquoteUsage(f)
name := "value"
v := reflect.TypeOf(f.Value).String()
b.WriteString(" ")
switch v {
case "*flag.boolFlag":
name = "bool"
case "*flag.durationValue":
name = "duration"
case "*flag.float64Value":
name = "float"
case "*flag.intValue", "*flag.int64Value":
name = "int"
case "*flag.stringValue":
name = "string"
case "*flag.uintValue", "*flag.uint64Value":
name = "uint"
case "*flag.mapValue":
// nv := f.Value.(*mapValue)
name = fmt.Sprintf("string key=val with %q as separator", mapDelim)
case "*flag.sliceValue":
// nv := f.Value.(*sliceValue)
name = fmt.Sprintf("string with %q as separator", sliceDelim)
}
b.WriteString(name)
// Boolean flags of one ASCII letter are so common we
// treat them specially, putting their usage on the same line.
if b.Len() <= 4 { // space, space, '-', 'x'.
b.WriteString("\t")
} else {
// Four spaces before the tab triggers good alignment
// for both 4- and 8-space tab stops.
b.WriteString("\n \t")
}
b.WriteString(strings.ReplaceAll(usage, "\n", "\n \t"))
if f.Value.String() == f.DefValue {
fmt.Fprintf(&b, " (default %q)", f.DefValue)
} else {
fmt.Fprintf(&b, " (default %q current %q)", f.DefValue, f.Value)
}
fmt.Fprint(c.fset.Output(), b.String(), "\n")
})
}
func (c *flagConfig) configure() {
flagSet := flag.CommandLine
flagSetName := os.Args[0]
flagSetErrorHandling := flag.ExitOnError
flagEnv := "env"
var flagUsage func()
var isSet bool
if c.opts.Context != nil {
if v, ok := c.opts.Context.Value(flagSetNameKey{}).(string); ok {
isSet = true
flagSetName = v
}
if v, ok := c.opts.Context.Value(flagSetErrorHandlingKey{}).(flag.ErrorHandling); ok {
isSet = true
flagSetErrorHandling = v
}
if v, ok := c.opts.Context.Value(flagSetKey{}).(*flag.FlagSet); ok {
flagSet = v
}
if v, ok := c.opts.Context.Value(flagSetUsageKey{}).(func()); ok {
flagUsage = v
}
if v, ok := c.opts.Context.Value(flagEnvKey{}).(string); ok {
flagEnv = v
}
}
c.fset = flagSet
if isSet {
c.fset.Init(flagSetName, flagSetErrorHandling)
}
if flagUsage != nil {
c.fset.Usage = flagUsage
} else {
c.fset.Usage = c.usage
}
c.env = flagEnv
c.name = flagSetName
}
func NewConfig(opts ...config.Option) config.Config { func NewConfig(opts ...config.Option) config.Config {
options := config.NewOptions(opts...) options := config.NewOptions(opts...)
if len(options.StructTag) == 0 { if len(options.StructTag) == 0 {
options.StructTag = DefaultStructTag options.StructTag = DefaultStructTag
} }
flagSet := flag.CommandLine
flagSetName := os.Args[0]
flagSetErrorHandling := flag.ExitOnError
var flagUsage func()
var isSet bool
if options.Context != nil { c := &flagConfig{opts: options}
if v, ok := options.Context.Value(flagSetNameKey{}).(string); ok { c.configure()
isSet = true
flagSetName = v
}
if v, ok := options.Context.Value(flagSetErrorHandlingKey{}).(flag.ErrorHandling); ok {
isSet = true
flagSetErrorHandling = v
}
if v, ok := options.Context.Value(flagSetKey{}).(*flag.FlagSet); ok {
flagSet = v
}
if v, ok := options.Context.Value(flagSetUsageKey{}).(func()); ok {
flagUsage = v
}
}
if isSet {
flagSet.Init(flagSetName, flagSetErrorHandling)
}
if flagUsage != nil {
flagSet.Usage = flagUsage
}
c := &flagConfig{opts: options, fset: flagSet}
return c return c
} }

View File

@ -54,3 +54,11 @@ type flagSetUsageKey struct{}
func FlagUsage(fn func()) config.Option { func FlagUsage(fn func()) config.Option {
return config.SetOption(flagSetUsageKey{}, fn) return config.SetOption(flagSetUsageKey{}, fn)
} }
type flagEnvKey struct{}
// FlagEnv set flag set usage func
func FlagEnv(n string) config.Option {
return config.SetOption(flagEnvKey{}, n)
}

16
util.go
View File

@ -15,7 +15,7 @@ type mapValue struct {
v reflect.Value v reflect.Value
} }
func (v mapValue) String() string { func (v *mapValue) String() string {
if v.v.Kind() != reflect.Invalid { if v.v.Kind() != reflect.Invalid {
var kv []string var kv []string
it := v.v.MapRange() it := v.v.MapRange()
@ -29,7 +29,11 @@ func (v mapValue) String() string {
return v.def return v.def
} }
func (v mapValue) Set(s string) error { func (v *mapValue) Get() interface{} {
return v.v.Interface()
}
func (v *mapValue) Set(s string) error {
ps := strings.Split(s, v.delim) ps := strings.Split(s, v.delim)
if len(ps) == 0 { if len(ps) == 0 {
return nil return nil
@ -72,7 +76,7 @@ type sliceValue struct {
v reflect.Value v reflect.Value
} }
func (v sliceValue) String() string { func (v *sliceValue) String() string {
if v.v.Kind() != reflect.Invalid { if v.v.Kind() != reflect.Invalid {
var kv []string var kv []string
for idx := 0; idx < v.v.Len(); idx++ { for idx := 0; idx < v.v.Len(); idx++ {
@ -83,7 +87,11 @@ func (v sliceValue) String() string {
return v.def return v.def
} }
func (v sliceValue) Set(s string) error { func (v *sliceValue) Get() interface{} {
return v.v.Interface()
}
func (v *sliceValue) Set(s string) error {
p := strings.Split(s, v.delim) p := strings.Split(s, v.delim)
v.v.Set(reflect.MakeSlice(v.v.Type(), len(p), len(p))) v.v.Set(reflect.MakeSlice(v.v.Type(), len(p), len(p)))
switch v.v.Type().Elem().Kind() { switch v.v.Type().Elem().Kind() {