From 41a4d393d9bfc98bbdc0de19f6495162402d75a2 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 7 Sep 2022 12:34:54 +0800 Subject: [PATCH] wip: refactoring app and global options logic --- .github/workflows/go.yml | 2 +- README.md | 2 +- README.zh-CN.md | 2 +- _examples/cliapp/main.go | 8 +- any.go | 7 + app.go | 80 ++++++---- base.go | 25 +++- typevars.go => builtin/typevars.go | 64 +------- cmd.go | 9 +- docs/TODO.md | 4 - gcli.go | 231 +++++++++++++++++++++++------ gflag.go | 12 +- gflag_test.go | 2 +- helper/utils.go | 2 +- interact/any.go | 7 + resource/Changelog-TODO.md | 4 +- resource/resource.md | 11 ++ show/any.go | 7 + util.go | 6 +- 19 files changed, 319 insertions(+), 166 deletions(-) create mode 100644 any.go rename typevars.go => builtin/typevars.go (67%) delete mode 100644 docs/TODO.md create mode 100644 interact/any.go create mode 100644 resource/resource.md create mode 100644 show/any.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 660c602..0fa591a 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go_version: [1.18, 1.19] + go_version: [1.17, 1.18, 1.19] steps: - name: Check out codes diff --git a/README.md b/README.md index 5360958..a712e03 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Actions Status](https://github.com/gookit/gcli/workflows/action-tests/badge.svg)](https://github.com/gookit/gcli/actions) [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/gcli)](https://github.com/gookit/gcli) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/4f071e6858fb4117b6c1376c9316d8ef)](https://www.codacy.com/gh/gookit/gcli/dashboard?utm_source=github.com&utm_medium=referral&utm_content=gookit/gcli&utm_campaign=Badge_Grade) -[![GoDoc](https://godoc.org/github.com/gookit/gcli?status.svg)](https://pkg.go.dev/github.com/gookit/gcli/v3) +[![Go Reference](https://pkg.go.dev/badge/github.com/gookit/goutil.svg)](https://pkg.go.dev/github.com/gookit/goutil) [![Go Report Card](https://goreportcard.com/badge/github.com/gookit/gcli)](https://goreportcard.com/report/github.com/gookit/gcli) [![Coverage Status](https://coveralls.io/repos/github/gookit/gcli/badge.svg?branch=master)](https://coveralls.io/github/gookit/gcli?branch=master) diff --git a/README.zh-CN.md b/README.zh-CN.md index 670e572..a033126 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -4,7 +4,7 @@ [![Actions Status](https://github.com/gookit/gcli/workflows/action-tests/badge.svg)](https://github.com/gookit/gcli/actions) [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/gcli)](https://github.com/gookit/gcli) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/4f071e6858fb4117b6c1376c9316d8ef)](https://www.codacy.com/gh/gookit/gcli/dashboard?utm_source=github.com&utm_medium=referral&utm_content=gookit/gcli&utm_campaign=Badge_Grade) -[![GoDoc](https://godoc.org/github.com/gookit/gcli?status.svg)](https://pkg.go.dev/github.com/gookit/gcli/v3) +[![Go Reference](https://pkg.go.dev/badge/github.com/gookit/goutil.svg)](https://pkg.go.dev/github.com/gookit/goutil) [![Go Report Card](https://goreportcard.com/badge/github.com/gookit/gcli)](https://goreportcard.com/report/github.com/gookit/gcli) [![Coverage Status](https://coveralls.io/repos/github/gookit/gcli/badge.svg?branch=master)](https://coveralls.io/github/gookit/gcli?branch=master) diff --git a/_examples/cliapp/main.go b/_examples/cliapp/main.go index acf092a..c10dd8d 100644 --- a/_examples/cliapp/main.go +++ b/_examples/cliapp/main.go @@ -10,6 +10,8 @@ import ( // "github.com/gookit/gcli/v3/builtin/reverseproxy" ) +var customGOpt string + // local run: // // go run ./_examples/cliapp @@ -42,10 +44,8 @@ func main() { // disable global options // gcli.GOpts().SetDisable() - var customGOpt string - app.GOptsBinder = func(gf *gcli.Flags) { - // gcli.Logf(gcli.VerbInfo, "custom add and global option flag") - gf.StrVar(&customGOpt, &gcli.FlagMeta{Name: "custom", Desc: "desc message for the option"}) + app.BeforeAddOpts = func(opts *gcli.Flags) { + opts.StrVar(&customGOpt, &gcli.FlagMeta{Name: "custom", Desc: "desc message for the option"}) } // app.Strict = true diff --git a/any.go b/any.go new file mode 100644 index 0000000..12a12c5 --- /dev/null +++ b/any.go @@ -0,0 +1,7 @@ +//go:build !go1.18 +// +build !go1.18 + +package gcli + +// alias of interface{}, use for go < 1.18 +type any = interface{} diff --git a/app.go b/app.go index fdda684..e292be2 100644 --- a/app.go +++ b/app.go @@ -9,6 +9,7 @@ import ( "github.com/gookit/color" "github.com/gookit/gcli/v3/events" "github.com/gookit/gcli/v3/helper" + "github.com/gookit/goutil/cflag" "github.com/gookit/goutil/cliutil" "github.com/gookit/goutil/strutil" ) @@ -41,8 +42,10 @@ type Logo struct { // AppConfig struct type AppConfig struct { - RunBefore func() bool - RunAfter func() bool + BeforeRun func() bool + AfterRun func() bool + BeforeAddOpts func(opts *Flags) + AfterBindOpts func(app *App) bool } // App the cli app definition @@ -55,6 +58,12 @@ type App struct { // for manager commands commandBase + AppConfig + + fs *Flags + // app flag options + opts *GlobalOpts + // Name app name Name string // Desc app description @@ -99,6 +108,7 @@ func NewApp(fns ...func(app *App)) *App { app := &App{ Name: "GCliApp", Desc: "This is my console application", + opts: newDefaultGlobalOpts(), // set a default version. // Version: "1.0.0", // config @@ -108,16 +118,21 @@ func NewApp(fns ...func(app *App)) *App { commandBase: newCommandBase(), } + app.fs = NewFlags("appOptions").WithConfigFn(func(opt *FlagsConfig) { + opt.WithoutType = true + opt.Alignment = AlignLeft + }) + // internal core Logf(VerbCrazy, "create new core on init application") app.core = core{ cmdLine: CLI, // init Hooks: &Hooks{}, - gFlags: NewFlags("app.GOptions").WithConfigFn(func(opt *FlagsConfig) { - opt.WithoutType = true - opt.Alignment = AlignLeft - }), + // gFlags: NewFlags("appOptions").WithConfigFn(func(opt *FlagsConfig) { + // opt.WithoutType = true + // opt.Alignment = AlignLeft + // }), } // init commandBase @@ -153,16 +168,14 @@ func (app *App) Config(fn func(a *App)) { func (app *App) bindingGlobalOpts() { Logf(VerbDebug, "will begin binding global options") // global options flag - // gf := flag.NewFlagSet(app.Args[0], flag.ContinueOnError) - gf := app.GlobalFlags() + fs := app.fs // binding global options - // bindingCommonGOpts(gf) - gOpts.bindingFlags(gf) + app.opts.bindingFlags(fs) // add more ... - gf.BoolOpt(&gOpts.showVer, "version", "V", false, "Display app version information") + fs.BoolOpt(&gOpts.ShowVersion, "version", "V", false, "Display app version information") // This is a internal option - gf.BoolVar(&gOpts.inCompletion, &FlagMeta{ + fs.BoolVar(&gOpts.inCompletion, &FlagMeta{ Name: "in-completion", Desc: "generate completion scripts for bash/zsh", // hidden it @@ -170,8 +183,8 @@ func (app *App) bindingGlobalOpts() { }) // support binding custom global options - if app.GOptsBinder != nil { - app.GOptsBinder(gf) + if app.BeforeAddOpts != nil { + app.BeforeAddOpts(fs) } } @@ -225,7 +238,7 @@ func (app *App) AddCommand(c *Command) { // init command c.app = app // inherit global flags from application - c.core.gFlags = app.gFlags + // c.core.gFlags = app.gFlags // do add command app.commandBase.addCommand(app.Name, c) @@ -275,46 +288,59 @@ func (app *App) AddAliases(name string, aliases ...string) { // } /************************************************************* - * run command + * parse global options *************************************************************/ -// parseGlobalOpts parse global options -func (app *App) parseGlobalOpts(args []string) (ok bool) { +// parseAppOpts parse global options +func (app *App) doParseOpts(args []string) error { + err := app.fs.Parse(args) + if err != nil { + if cflag.IsFlagHelpErr(err) { + return nil + } + Logf(VerbWarn, "parse global options err: %s", err.Error()) + } + + return err +} + +// parseAppOpts parse global options +func (app *App) parseAppOpts(args []string) (ok bool) { Logf(VerbDebug, "will begin parse application options") // parse global options - err := app.core.doParseGOpts(args) + err := app.doParseOpts(args) if err != nil { // has error. color.Error.Tips(err.Error()) return } - app.args = app.gFlags.FSetArgs() + app.args = app.fs.FSetArgs() if app.Fire(events.OnGOptionsParsed, map[string]any{"args": app.args}) { Logf(VerbDebug, "stop continue on the event %s return True", events.OnGOptionsParsed) return } // check global options - if gOpts.showHelp { + if app.opts.ShowHelp { app.showApplicationHelp() return } - if gOpts.showVer { + if app.opts.ShowVersion { app.showVersionInfo() return } // disable color - if gOpts.NoColor { + if app.opts.NoColor { color.Enable = false } - Debugf("global option parsed, verbose level: %s", gOpts.verbose.String()) + Debugf("global option parsed, Verbose level: %s", app.opts.Verbose.String()) // TODO show auto-completion for bash/zsh - if gOpts.inCompletion { + if app.opts.inCompletion { app.showAutoCompletion(app.args) return } @@ -468,7 +494,7 @@ func (app *App) Run(args []string) (code int) { Debugf("will begin run cli application. args: %v", args) // parse global flags - if false == app.parseGlobalOpts(args) { + if false == app.parseAppOpts(args) { return app.exitOnEnd(code) } @@ -684,7 +710,7 @@ func (app *App) showApplicationHelp() { // render help text template s := helper.RenderText(AppHelpTemplate, map[string]any{ "Cs": app.commands, - "GOpts": app.gFlags.String(), + "GOpts": app.fs.String(), // app version "Version": app.Version, "HasSubs": app.hasSubcommands, diff --git a/base.go b/base.go index 28a7395..06dc5be 100644 --- a/base.go +++ b/base.go @@ -1,6 +1,7 @@ package gcli import ( + "context" "os" "path" "path/filepath" @@ -11,6 +12,7 @@ import ( "github.com/gookit/color" "github.com/gookit/gcli/v3/helper" "github.com/gookit/goutil/cflag" + "github.com/gookit/goutil/maputil" "github.com/gookit/goutil/mathutil" "github.com/gookit/goutil/structs" "github.com/gookit/goutil/strutil" @@ -134,6 +136,27 @@ func (md *mapData) ClearData() { * Command Line: command data *************************************************************/ +// Context struct +type Context struct { + maputil.Data + context.Context + // some common info + PID int +} + +// NewCtx instance +func NewCtx() *Context { + return &Context{ + Data: make(maputil.Data), + Context: context.Background(), + } +} + +// Value get by key +func (ctx *Context) Value(key any) any { + return ctx.Data.Get(key.(string)) +} + // cmdLine store common data for CLI type cmdLine struct { // pid for current application @@ -273,7 +296,7 @@ func newCommandBase() commandBase { nameMaxWidth: 12, // cmdAliases: make(maputil.Aliases), cmdAliases: structs.NewAliases(aliasNameCheck), - ExitOnEnd: true, + // ExitOnEnd: false, } } diff --git a/typevars.go b/builtin/typevars.go similarity index 67% rename from typevars.go rename to builtin/typevars.go index 76cbb8b..21f9030 100644 --- a/typevars.go +++ b/builtin/typevars.go @@ -1,9 +1,8 @@ -package gcli +package builtin import ( "fmt" "strconv" - "strings" "github.com/gookit/goutil/strutil" ) @@ -134,64 +133,3 @@ func (s *String) Split(sep string) []string { func (s *String) Ints(sep string) []int { return strutil.Ints(string(*s), sep) } - -/************************************************************************* - * verbose level - *************************************************************************/ - -// VerbLevel type. -type VerbLevel uint - -// Int verbose level to int. -func (vl *VerbLevel) Int() int { - return int(*vl) -} - -// String verbose level to string. -func (vl *VerbLevel) String() string { - return fmt.Sprintf("%d=%s", *vl, vl.Name()) -} - -// Upper verbose level to string. -func (vl *VerbLevel) Upper() string { - return strings.ToUpper(vl.Name()) -} - -// Name verbose level to string. -func (vl *VerbLevel) Name() string { - switch *vl { - case VerbQuiet: - return "quiet" - case VerbError: - return "error" - case VerbWarn: - return "warn" - case VerbInfo: - return "info" - case VerbDebug: - return "debug" - case VerbCrazy: - return "crazy" - } - return "unknown" -} - -// Set value from option binding. -func (vl *VerbLevel) Set(value string) error { - // int: level value. - if iv, err := strconv.Atoi(value); err == nil { - if iv > int(VerbCrazy) { - *vl = VerbCrazy - } else if iv < 0 { // fallback to default level. - *vl = DefaultVerb - } else { // 0 - 5 - *vl = VerbLevel(iv) - } - - return nil - } - - // string: level name. - *vl = name2verbLevel(value) - return nil -} diff --git a/cmd.go b/cmd.go index e4c757e..d4a4d75 100644 --- a/cmd.go +++ b/cmd.go @@ -71,6 +71,7 @@ type Command struct { // Category for the command Category string // Config func, will call on `initialize`. + // // - you can config options and other init works Config func(c *Command) // Hidden the command on render help @@ -215,8 +216,8 @@ func (c *Command) AddCommand(sub *Command) { sub.parent = c // inherit standalone value sub.standalone = c.standalone - // inherit global flags from application - sub.core.gFlags = c.gFlags + // inherit global flags from parent + // sub.core.gFlags = c.gFlags // initialize command c.initialize() @@ -239,7 +240,7 @@ func (c *Command) Match(names []string) *Command { return c.commandBase.Match(names) } -// MatchByPath command by path. eg. "top:sub" +// MatchByPath command by path. eg: "top:sub" func (c *Command) MatchByPath(path string) *Command { return c.Match(splitPath2names(path)) } @@ -429,7 +430,7 @@ func (c *Command) innerDispatch(args []string) (err error) { // remaining args if c.standalone { - if gOpts.showHelp { + if gOpts.ShowHelp { c.ShowHelp() return } diff --git a/docs/TODO.md b/docs/TODO.md deleted file mode 100644 index 47ac622..0000000 --- a/docs/TODO.md +++ /dev/null @@ -1,4 +0,0 @@ -# TODO - -- hook on set flag value -- multi option aliases diff --git a/gcli.go b/gcli.go index f6c29a3..f96292e 100644 --- a/gcli.go +++ b/gcli.go @@ -10,7 +10,13 @@ package gcli import ( + "flag" + "fmt" "os" + "strconv" + "strings" + + "github.com/gookit/gcli/v3/builtin" ) const ( @@ -41,10 +47,12 @@ const ( var ( // CLI create an default instance CLI = newCmdLine() - // DefaultVerb the default verbose level + // DefaultVerb the default Verbose level DefaultVerb = VerbError + + cfg = newCliConfig() // global options - gOpts = newDefaultGOptions() + gOpts = newDefaultGlobalOpts() // Version the gcli version version = "3.0.0" // CommitID the gcli last commit ID @@ -53,21 +61,54 @@ var ( // init func init() { - // set verbose from ENV var. - envVerb := os.Getenv(VerbEnvName) - if envVerb != "" { - _ = gOpts.verbose.Set(envVerb) + // set Verbose from ENV var. + if verb := os.Getenv(VerbEnvName); verb != "" { + _ = cfg.Verbose.Set(verb) + } +} + +// CliConfig cli Config config +type CliConfig struct { + NoColor bool + // Verbose gcli message report level + Verbose VerbLevel + // NoProgress dont display progress + NoProgress bool + // NoInteractive close interactive confirm + NoInteractive bool +} + +func newCliConfig() *CliConfig { + return &CliConfig{ + Verbose: VerbError, + } +} + +// Config global config +func Config(fn func(cfg *CliConfig)) { + if fn != nil { + fn(cfg) + } +} + +// ResetConfig global config +func ResetConfig() { + cfg = newCliConfig() + + // set Verbose from ENV var. + if verb := os.Getenv(VerbEnvName); verb != "" { + _ = cfg.Verbose.Set(verb) } } // GOpts get the global options -func GOpts() *GOptions { +func GOpts() *GlobalOpts { return gOpts } // ResetGOpts instance func ResetGOpts() { - gOpts = newDefaultGOptions() + gOpts = newDefaultGlobalOpts() } // Version of the gcli @@ -78,8 +119,8 @@ func Version() string { // CommitID of the gcli func CommitID() string { return commitID } -// Verbose returns verbose level -func Verbose() VerbLevel { return gOpts.Verbose() } +// Verbose returns Verbose level +func Verbose() VerbLevel { return gOpts.Verbose } // SetCrazyMode level func SetCrazyMode() { gOpts.SetVerbose(VerbCrazy) } @@ -103,28 +144,29 @@ func StrictMode() bool { return gOpts.strictMode } func SetStrictMode(strict bool) { gOpts.SetStrictMode(strict) } // IsGteVerbose get is strict mode -func IsGteVerbose(verb VerbLevel) bool { return gOpts.verbose >= verb } +func IsGteVerbose(verb VerbLevel) bool { return gOpts.Verbose >= verb } // IsDebugMode get is debug mode -func IsDebugMode() bool { return gOpts.verbose >= VerbDebug } +func IsDebugMode() bool { return gOpts.Verbose >= VerbDebug } /************************************************************************* - * global options + * app options *************************************************************************/ -// GOptions global flag options -type GOptions struct { +// GlobalOpts global flag options +type GlobalOpts struct { Disable bool NoColor bool - verbose VerbLevel // message report level - showVer bool - showHelp bool + Verbose VerbLevel // message report level + ShowHelp bool // TODO Run application an interactive shell environment inShell bool - // dont display progress - noProgress bool - // close interactive confirm - noInteractive bool + // ShowVersion show version information + ShowVersion bool + // NoProgress dont display progress + NoProgress bool + // NoInteractive close interactive confirm + NoInteractive bool // TODO auto format shorts `-a` to POSIX or UNIX style. // StrictMode use strict mode for parse flags // If True(default): @@ -137,46 +179,40 @@ type GOptions struct { inCompletion bool } -func newDefaultGOptions() *GOptions { - return &GOptions{ +func newDefaultGlobalOpts() *GlobalOpts { + return &GlobalOpts{ strictMode: false, // init error level. - verbose: DefaultVerb, + Verbose: cfg.Verbose, + NoColor: cfg.NoColor, } } -// Verbose value -func (g *GOptions) Verbose() VerbLevel { - return g.verbose -} - // SetVerbose value -func (g *GOptions) SetVerbose(verbose VerbLevel) { - g.verbose = verbose +func (g *GlobalOpts) SetVerbose(verbose VerbLevel) { + g.Verbose = verbose } // SetStrictMode option -func (g *GOptions) SetStrictMode(strictMode bool) { +func (g *GlobalOpts) SetStrictMode(strictMode bool) { g.strictMode = strictMode } -// NoInteractive value -func (g *GOptions) NoInteractive() bool { - return g.noInteractive -} - -// NoProgress value -func (g *GOptions) NoProgress() bool { - return g.noProgress -} - // SetDisable global options -func (g *GOptions) SetDisable() { +func (g *GlobalOpts) SetDisable() { g.Disable = true } -func (g *GOptions) bindingFlags(fs *Flags) { - fs.BoolOpt(&g.showHelp, "help", "h", false, "Display the help information") +func (g *GlobalOpts) bindingFlags(fs *Flags) { + fs.BoolOpt(&g.ShowHelp, "help", "h", false, "Display the help information") + + // return ErrHelp on ShowHelp=true + fs.AfterParse = func(_ *Flags) error { + if g.ShowHelp { + return flag.ErrHelp + } + return nil + } // disabled if g.Disable { @@ -184,9 +220,106 @@ func (g *GOptions) bindingFlags(fs *Flags) { } // up: allow use int and string. - fs.VarOpt(&g.verbose, "verbose", "", "Set logs reporting level(quiet 0 - 5 crazy)") + fs.VarOpt(&g.Verbose, "Verbose", "", "Set logs reporting level(quiet 0 - 5 crazy)") fs.BoolOpt(&g.inShell, "ishell", "", false, "Run in an interactive shell environment(`TODO`)") fs.BoolOpt(&g.NoColor, "no-color", "nc", g.NoColor, "Disable color when outputting message") - fs.BoolOpt(&g.noProgress, "no-progress", "np", g.noProgress, "Disable display progress message") - fs.BoolOpt(&g.noInteractive, "no-interactive", "ni", g.noInteractive, "Disable interactive confirmation operation") + fs.BoolOpt(&g.NoProgress, "no-progress", "np", g.NoProgress, "Disable display progress message") + fs.BoolOpt(&g.NoInteractive, "no-interactive", "ni", g.NoInteractive, "Disable interactive confirmation operation") +} + +/************************************************************************* + * options: some special flag vars + * - implemented flag.Value interface + *************************************************************************/ + +// Ints The int flag list, implemented flag.Value interface +type Ints = builtin.Ints + +// Strings The string flag list, implemented flag.Value interface +type Strings = builtin.Strings + +// Booleans The bool flag list, implemented flag.Value interface +type Booleans = builtin.Booleans + +// EnumString The string flag list, implemented flag.Value interface +type EnumString = builtin.EnumString + +// String type, a special string +// +// Usage: +// +// // case 1: +// var names gcli.String +// c.VarOpt(&names, "names", "", "multi name by comma split") +// +// --names "tom,john,joy" +// names.Split(",") -> []string{"tom","john","joy"} +// +// // case 2: +// var ids gcli.String +// c.VarOpt(&ids, "ids", "", "multi id by comma split") +// +// --names "23,34,56" +// names.Ints(",") -> []int{23,34,56} +type String = builtin.String + +/************************************************************************* + * Verbose level + *************************************************************************/ + +// VerbLevel type. +type VerbLevel uint + +// Int Verbose level to int. +func (vl *VerbLevel) Int() int { + return int(*vl) +} + +// String Verbose level to string. +func (vl *VerbLevel) String() string { + return fmt.Sprintf("%d=%s", *vl, vl.Name()) +} + +// Upper Verbose level to string. +func (vl *VerbLevel) Upper() string { + return strings.ToUpper(vl.Name()) +} + +// Name Verbose level to string. +func (vl *VerbLevel) Name() string { + switch *vl { + case VerbQuiet: + return "quiet" + case VerbError: + return "error" + case VerbWarn: + return "warn" + case VerbInfo: + return "info" + case VerbDebug: + return "debug" + case VerbCrazy: + return "crazy" + } + return "unknown" +} + +// Set value from option binding. +func (vl *VerbLevel) Set(value string) error { + // int: level value. + if iv, err := strconv.Atoi(value); err == nil { + if iv > int(VerbCrazy) { + *vl = VerbCrazy + } else if iv < 0 { // fallback to default level. + *vl = DefaultVerb + } else { // 0 - 5 + *vl = VerbLevel(iv) + } + + return nil + } + + // string: level name. + *vl = name2verbLevel(value) + return nil } diff --git a/gflag.go b/gflag.go index 8fcc6e7..3f66d13 100644 --- a/gflag.go +++ b/gflag.go @@ -69,7 +69,8 @@ type Flags struct { // Desc message Desc string // ExitFunc for handle exit - ExitFunc func(code int) + // ExitFunc func(code int) + AfterParse func(fs *Flags) error // cfg option for the flags cfg *FlagsConfig @@ -109,7 +110,7 @@ func NewFlags(nameWithDesc ...string) *Flags { out: os.Stdout, cfg: newDefaultFlagConfig(), } - fs.ExitFunc = os.Exit + // fs.ExitFunc = os.Exit fName := "gflag" if num := len(nameWithDesc); num > 0 { @@ -234,8 +235,11 @@ func (fs *Flags) Parse(args []string) (err error) { return err } - if gOpts.showHelp { - return flag.ErrHelp + // after hook + if fs.AfterParse != nil { + if err := fs.AfterParse(fs); err != nil { + return err + } } // call flags validate diff --git a/gflag_test.go b/gflag_test.go index 952d120..d9253ae 100644 --- a/gflag_test.go +++ b/gflag_test.go @@ -289,7 +289,7 @@ func TestFlags_Run(t *testing.T) { is := assert.New(t) fg := gcli.NewFlags("test", "desc message") - fg.ExitFunc = func(code int) {} + // fg.ExitFunc = func(code int) {} fg.IntOpt(&flagOpts.intv, "intv", "i", 0, "desc message for intv") fg.StrOpt(&flagOpts.strv, "strv", "s", "", "desc message for strv") diff --git a/helper/utils.go b/helper/utils.go index b191e9e..c7e736d 100644 --- a/helper/utils.go +++ b/helper/utils.go @@ -23,7 +23,7 @@ var ( // linuxSttyMsgMatch = regexp.MustCompile(linuxSttyMsgPattern) ) -// RenderText render text template with data +// RenderText render text template with data. TODO use strutil.RenderText() func RenderText(input string, data any, fns template.FuncMap, isFile ...bool) string { t := template.New("cli") t.Funcs(template.FuncMap{ diff --git a/interact/any.go b/interact/any.go new file mode 100644 index 0000000..d93a2ec --- /dev/null +++ b/interact/any.go @@ -0,0 +1,7 @@ +//go:build !go1.18 +// +build !go1.18 + +package interact + +// alias of interface{}, use for go < 1.18 +type any = interface{} diff --git a/resource/Changelog-TODO.md b/resource/Changelog-TODO.md index 5a2d99c..264d8cd 100644 --- a/resource/Changelog-TODO.md +++ b/resource/Changelog-TODO.md @@ -2,8 +2,8 @@ ## TODO -- [x] flag option support alias name. - - It isn't shorts name. eg: `name='dry-run' alias='dr'` +- hook on set flag value +- [x] option support multi shorts namex - [ ] support flag option category - [ ] support command category by `c.Category` - [ ] print parent's options on subcommand help panel diff --git a/resource/resource.md b/resource/resource.md new file mode 100644 index 0000000..1d2fd2d --- /dev/null +++ b/resource/resource.md @@ -0,0 +1,11 @@ +# resource + +## Related + +- https://github.com/spf13/cobra +- https://github.com/mitchellh/cli + +## Completion + +- https://github.com/posener/complete bash completion written in go + bash completion for go command + diff --git a/show/any.go b/show/any.go new file mode 100644 index 0000000..292ac9c --- /dev/null +++ b/show/any.go @@ -0,0 +1,7 @@ +//go:build !go1.18 +// +build !go1.18 + +package show + +// alias of interface{}, use for go < 1.18 +type any = interface{} diff --git a/util.go b/util.go index f415475..65ea2ee 100644 --- a/util.go +++ b/util.go @@ -7,7 +7,7 @@ import ( "github.com/gookit/color" "github.com/gookit/goutil/arrutil" - "github.com/gookit/goutil/common" + "github.com/gookit/goutil/comdef" "github.com/gookit/goutil/stdutil" "github.com/gookit/goutil/strutil" ) @@ -36,7 +36,7 @@ func Logf(level VerbLevel, format string, v ...any) { // print log message func logf(level VerbLevel, format string, v ...any) { - if gOpts.verbose < level { + if gOpts.Verbose < level { return } @@ -104,7 +104,7 @@ func sepStr(seps []string) string { if len(seps) > 0 { return seps[0] } - return common.DefaultSep + return comdef.DefaultSep } // allowed keys on struct tag.