From 24474f76f193e3c305291adcbe5f3f043c6e475f Mon Sep 17 00:00:00 2001 From: Inhere Date: Sat, 21 Jan 2023 16:46:53 +0800 Subject: [PATCH] :necktie: up: update the cli option binding logic - add new method for quick add option --- builtin/gen_auto_complete.go | 13 +++++--- gflag/opts.go | 60 +++++++++++++++++++++++++++++------- show/base.go | 5 +++ show/list.go | 2 +- 4 files changed, 63 insertions(+), 17 deletions(-) diff --git a/builtin/gen_auto_complete.go b/builtin/gen_auto_complete.go index 3ff75f9..de8765e 100644 --- a/builtin/gen_auto_complete.go +++ b/builtin/gen_auto_complete.go @@ -2,7 +2,7 @@ package builtin import ( "fmt" - "io/ioutil" + "os" "strings" "github.com/gookit/color" @@ -34,8 +34,8 @@ var shellTpls = map[string]string{ } // GenAutoComplete create command -func GenAutoComplete() *gcli.Command { - c := gcli.Command{ +func GenAutoComplete(fns ...func(c *gcli.Command)) *gcli.Command { + c := &gcli.Command{ Func: doGen, Name: "genac", Aliases: []string{"gen-ac"}, @@ -71,7 +71,10 @@ func GenAutoComplete() *gcli.Command { "output shell auto completion script file name.", ) - return &c + for _, fn := range fns { + fn(c) + } + return c } func doGen(c *gcli.Command, _ []string) (err error) { @@ -125,7 +128,7 @@ func doGen(c *gcli.Command, _ []string) (err error) { } // Open the file for reading and writing, if it does not exist, create it - err = ioutil.WriteFile(genOpts.output, []byte(str), 0664) + err = os.WriteFile(genOpts.output, []byte(str), 0664) if err != nil { return c.NewErrf("Write file error: %s", err.Error()) } diff --git a/gflag/opts.go b/gflag/opts.go index 89be949..d67d26d 100644 --- a/gflag/opts.go +++ b/gflag/opts.go @@ -14,6 +14,11 @@ import ( "github.com/gookit/goutil/strutil" ) +const ( + shortSepRune = ',' + shortSepChar = "," +) + // CliOpts cli options management type CliOpts struct { // name inherited from gcli.Command @@ -143,7 +148,20 @@ func (ops *CliOpts) StrOpt(p *string, name, shorts string, defValAndDesc ...stri } } - ops.strOpt(p, newCliOpt(name, desc, defVal, shorts)) + ops.StrOpt2(p, name, desc, func(opt *CliOpt) { + opt.DefVal = defVal + opt.Shorts = strings.Split(shorts, shortSepChar) + }) +} + +// StrOpt2 binding a string option, and allow with CliOptFn for config option. +func (ops *CliOpts) StrOpt2(p *string, nameWithShorts, desc string, confFuncs ...CliOptFn) { + opt := &CliOpt{Name: nameWithShorts, Desc: desc, DefVal: ""} + for _, optFn := range confFuncs { + optFn(opt) + } + + ops.strOpt(p, opt) } // binding option and shorts @@ -171,11 +189,21 @@ func (ops *CliOpts) Int(name, shorts string, defVal int, desc string) *int { } // IntVar binding an int option flag -func (ops *CliOpts) IntVar(ptr *int, opt *CliOpt) { ops.intOpt(ptr, opt) } +func (ops *CliOpts) IntVar(p *int, opt *CliOpt) { ops.intOpt(p, opt) } // IntOpt binding an int option -func (ops *CliOpts) IntOpt(ptr *int, name, shorts string, defVal int, desc string) { - ops.intOpt(ptr, newCliOpt(name, desc, defVal, shorts)) +func (ops *CliOpts) IntOpt(p *int, name, shorts string, defVal int, desc string) { + ops.intOpt(p, newCliOpt(name, desc, defVal, shorts)) +} + +// IntOpt2 binding an int option and with config func. +func (ops *CliOpts) IntOpt2(p *int, nameWithShorts, desc string, confFuncs ...CliOptFn) { + opt := &CliOpt{Name: nameWithShorts, Desc: desc, DefVal: 0} + for _, optFn := range confFuncs { + optFn(opt) + } + + ops.intOpt(p, opt) } func (ops *CliOpts) intOpt(ptr *int, opt *CliOpt) { @@ -300,6 +328,12 @@ func (ops *CliOpts) varOpt(v flag.Value, opt *CliOpt) { // check flag option name and short-names func (ops *CliOpts) checkFlagInfo(opt *CliOpt) string { + // check flag name + name := opt.initCheck() + if _, ok := ops.opts[name]; ok { + helper.Panicf("redefined option flag '%s'", name) + } + // NOTICE: must init some required fields if ops.names == nil { ops.names = map[string]int{} @@ -307,12 +341,6 @@ func (ops *CliOpts) checkFlagInfo(opt *CliOpt) string { ops.InitFlagSet("flags-" + opt.Name) } - // check flag name - name := opt.initCheck() - if _, ok := ops.opts[name]; ok { - helper.Panicf("redefined option flag '%s'", name) - } - // is a short name helpLen := opt.helpNameLen() // fix: must exclude Hidden option @@ -416,6 +444,9 @@ func (ops *CliOpts) Opts() map[string]*CliOpt { return ops.opts } * flag options metadata ***********************************************************************/ +// CliOptFn type +type CliOptFn func(opt *CliOpt) + // CliOpt define for a flag option type CliOpt struct { // go flag value @@ -450,11 +481,18 @@ func newCliOpt(name, desc string, defVal any, shortcut string) *CliOpt { Desc: desc, // other info DefVal: defVal, - Shorts: strings.Split(shortcut, ","), + Shorts: strings.Split(shortcut, shortSepChar), } } func (m *CliOpt) initCheck() string { + // feat: support add shorts by option name. eg: "name,n" + if strings.ContainsRune(m.Name, shortSepRune) { + ss := strings.Split(m.Name, shortSepChar) + m.Name = ss[0] + m.Shorts = append(m.Shorts, ss[1:]...) + } + if m.Desc != "" { desc := strings.Trim(m.Desc, "; ") if strings.ContainsRune(desc, ';') { diff --git a/show/base.go b/show/base.go index f28377f..9d61d7e 100644 --- a/show/base.go +++ b/show/base.go @@ -252,6 +252,11 @@ func (item *Item) Kind() reflect.Kind { return item.rftVal.Kind() } +// IsKind check +func (item *Item) IsKind(kind reflect.Kind) bool { + return item.rftVal.Kind() == kind +} + // IsArray get is array, slice func (item *Item) IsArray() bool { return item.rftVal.Kind() == reflect.Array || item.rftVal.Kind() == reflect.Slice diff --git a/show/list.go b/show/list.go index 156b248..08b6946 100644 --- a/show/list.go +++ b/show/list.go @@ -138,7 +138,7 @@ func (l *List) Format() { f.SetOutput(l.buf) }).Format() l.buf.WriteByte('\n') - } else if item.Kind() == reflect.Map { + } else if item.IsKind(reflect.Map) { maputil.NewFormatter(item.rftVal).WithFn(func(f *maputil.MapFormatter) { f.Indent = mlIndent f.ClosePrefix = " "