Skip to content

Commit

Permalink
refactor: refactor the gflag parse to sub-pkg /gflag
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Sep 21, 2022
1 parent 963b35e commit 931b003
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 128 deletions.
10 changes: 6 additions & 4 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (

"github.com/gookit/color"
"github.com/gookit/gcli/v3/events"
"github.com/gookit/gcli/v3/gflag"
"github.com/gookit/gcli/v3/helper"
"github.com/gookit/goutil/cflag"
"github.com/gookit/goutil/cliutil"
)
Expand Down Expand Up @@ -103,9 +105,9 @@ func NewApp(fns ...func(app *App)) *App {

// set a default version
app.Version = "1.0.0"
app.fs = NewFlags("appOpts").WithConfigFn(func(opt *FlagsConfig) {
app.fs = gflag.New("appOpts").WithConfigFn(func(opt *gflag.FlagsConfig) {
opt.WithoutType = true
opt.Alignment = AlignLeft
opt.Alignment = gflag.AlignLeft
})

// init base
Expand Down Expand Up @@ -145,7 +147,7 @@ func (app *App) bindingGOpts() {
app.opts.bindingFlags(fs)
// add more ...
// This is an internal option
fs.BoolVar(&gOpts.inCompletion, &FlagMeta{
fs.BoolVar(&gOpts.inCompletion, &gflag.FlagMeta{
Name: "in-completion",
Desc: "generate completion scripts for bash/zsh",
// hidden it
Expand Down Expand Up @@ -383,7 +385,7 @@ func (app *App) findCommandName() (name string) {
}

// check is valid ID/name string.
if !goodCmdId.MatchString(name) {
if !helper.IsGoodCmdId(name) {
Logf(VerbWarn, "the input command name(%s) string is invalid", name)
return ""
}
Expand Down
3 changes: 2 additions & 1 deletion cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/arrutil"
"github.com/gookit/goutil/structs"
"github.com/gookit/goutil/strutil"
Expand Down Expand Up @@ -622,7 +623,7 @@ func (c *Command) goodName() string {
panicf("the command name can not be empty")
}

if !goodCmdName.MatchString(name) {
if !helper.IsGoodCmdName(name) {
panicf("the command name '%s' is invalid, must match: %s", name, regGoodCmdName)
}

Expand Down
3 changes: 2 additions & 1 deletion gargs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package gcli
import (
"strings"

"github.com/gookit/gcli/v3/gflag"
"github.com/gookit/goutil/errorx"
"github.com/gookit/goutil/structs"
"github.com/gookit/goutil/strutil"
Expand Down Expand Up @@ -108,7 +109,7 @@ func (ags *Arguments) AddArg(name, desc string, requiredAndArrayed ...bool) *Arg

// AddArgByRule add an arg by simple string rule
func (ags *Arguments) AddArgByRule(name, rule string) *Argument {
mp := parseSimpleRule(name, rule)
mp := gflag.ParseSimpleRule(name, rule)

required := strutil.QuietBool(mp["required"])
newArg := NewArgument(name, mp["desc"], required)
Expand Down
23 changes: 17 additions & 6 deletions gcli.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
"strconv"
"strings"

"github.com/gookit/gcli/v3/helper"
"github.com/gookit/gcli/v3/gflag"
"github.com/gookit/goutil/envutil"
)

Expand Down Expand Up @@ -65,6 +65,17 @@ func init() {
}
}

// Flags alias of the gflag.Flags
type Flags = gflag.Flags

// FlagMeta alias of the gflag.FlagMeta
type FlagMeta = gflag.FlagMeta

// NewFlags create new gflag.Flags
func NewFlags(nameWithDesc ...string) *gflag.Flags {
return gflag.New(nameWithDesc...)
}

/*************************************************************************
* global options
*************************************************************************/
Expand Down Expand Up @@ -205,16 +216,16 @@ func IsDebugMode() bool { return gOpts.Verbose >= VerbDebug }
*************************************************************************/

// Ints The int flag list, implemented flag.Value interface
type Ints = helper.Ints
type Ints = gflag.Ints

// Strings The string flag list, implemented flag.Value interface
type Strings = helper.Strings
type Strings = gflag.Strings

// Booleans The bool flag list, implemented flag.Value interface
type Booleans = helper.Booleans
type Booleans = gflag.Booleans

// EnumString The string flag list, implemented flag.Value interface
type EnumString = helper.EnumString
type EnumString = gflag.EnumString

// String type, a special string
//
Expand All @@ -233,7 +244,7 @@ type EnumString = helper.EnumString
//
// --names "23,34,56"
// names.Ints(",") -> []int{23,34,56}
type String = helper.String
type String = gflag.String

/*************************************************************************
* Verbose level
Expand Down
27 changes: 14 additions & 13 deletions gflag.go → gflag/gflag.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package gcli
package gflag

import (
"bytes"
Expand All @@ -13,6 +13,7 @@ import (
"unsafe"

"github.com/gookit/color"
"github.com/gookit/gcli/v3/helper"
"github.com/gookit/goutil/cflag"
"github.com/gookit/goutil/mathutil"
"github.com/gookit/goutil/stdutil"
Expand Down Expand Up @@ -104,8 +105,8 @@ func newDefaultFlagConfig() *FlagsConfig {
}
}

// NewFlags create a new Flags
func NewFlags(nameWithDesc ...string) *Flags {
// New create a new Flags
func New(nameWithDesc ...string) *Flags {
fs := &Flags{
out: os.Stdout,
cfg: newDefaultFlagConfig(),
Expand Down Expand Up @@ -227,7 +228,7 @@ func (fs *Flags) Parse(args []string) (err error) {

if len(fs.shorts) > 0 && len(args) > 0 {
args = cflag.ReplaceShorts(args, fs.shorts)
Debugf("replace shortcuts. now, args: %v", args)
// TODO gcli.Debugf("replace shortcuts. now, args: %v", args)
}

// do parsing
Expand Down Expand Up @@ -331,7 +332,7 @@ func (fs *Flags) FromStruct(ptr any) error {
if fs.cfg.TagRuleType == TagRuleNamed {
mp = parseNamedRule(name, str)
} else if fs.cfg.TagRuleType == TagRuleSimple {
mp = parseSimpleRule(name, str)
mp = ParseSimpleRule(name, str)
} else {
return errTagRuleType
}
Expand Down Expand Up @@ -619,7 +620,7 @@ func (fs *Flags) Required(names ...string) {
for _, name := range names {
meta, ok := fs.metas[name]
if !ok {
panicf("undefined option flag '%s'", name)
helper.Panicf("undefined option flag '%s'", name)
}
meta.Required = true
}
Expand All @@ -637,7 +638,7 @@ func (fs *Flags) checkFlagInfo(meta *FlagMeta) string {
// check flag name
name := meta.initCheck()
if _, ok := fs.metas[name]; ok {
panicf("redefined option flag '%s'", name)
helper.Panicf("redefined option flag '%s'", name)
}

// is a short name
Expand Down Expand Up @@ -670,15 +671,15 @@ func (fs *Flags) checkShortNames(name string, shorts []string) {

for _, short := range shorts {
if name == short {
panicf("short name '%s' has been used as the current option name", short)
helper.Panicf("short name '%s' has been used as the current option name", short)
}

if _, ok := fs.names[short]; ok {
panicf("short name '%s' has been used as an option name", short)
helper.Panicf("short name '%s' has been used as an option name", short)
}

if n, ok := fs.shorts[short]; ok {
panicf("short name '%s' has been used by option '%s'", short, n)
helper.Panicf("short name '%s' has been used by option '%s'", short, n)
}

// storage short name
Expand Down Expand Up @@ -975,11 +976,11 @@ func (m *FlagMeta) initCheck() string {
func (m *FlagMeta) goodName() string {
name := strings.Trim(m.Name, "- ")
if name == "" {
panicf("option flag name cannot be empty")
helper.Panicf("option flag name cannot be empty")
}

if !goodName.MatchString(name) {
panicf("option flag name '%s' is invalid, must match: %s", name, regGoodName)
if !helper.IsGoodName(name) {
helper.Panicf("option flag name '%s' is invalid", name)
}

// update self name
Expand Down
5 changes: 3 additions & 2 deletions gflag_test.go → gflag/gflag_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package gcli_test
package gflag_test

import (
"bytes"
Expand All @@ -8,12 +8,13 @@ import (
"testing"

"github.com/gookit/gcli/v3"
"github.com/gookit/gcli/v3/gflag"
"github.com/gookit/goutil/dump"
"github.com/gookit/goutil/testutil/assert"
)

func TestFlags_Basic(t *testing.T) {
fs := gcli.NewFlags("testFlags")
fs := gflag.New("testFlags")

assert.Len(t, fs.Metas(), 0)
assert.Eq(t, 0, fs.Len())
Expand Down
2 changes: 1 addition & 1 deletion helper/type_vars.go → gflag/type_vars.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package helper
package gflag

import (
"fmt"
Expand Down
100 changes: 100 additions & 0 deletions gflag/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package gflag

import (
"strings"

"github.com/gookit/gcli/v3/helper"
"github.com/gookit/goutil/arrutil"
"github.com/gookit/goutil/comdef"
"github.com/gookit/goutil/strutil"
)

func sepStr(seps []string) string {
if len(seps) > 0 {
return seps[0]
}
return comdef.DefaultSep
}

// allowed keys on struct tag.
var flagTagKeys = arrutil.Strings{"name", "shorts", "desc", "default", "required"}

// parse tag named k-v value. item split by ';'
//
// eg: "name=int0;shorts=i;required=true;desc=int option message"
//
// supported field name:
//
// name
// desc
// shorts
// required
// default
func parseNamedRule(name, rule string) (mp map[string]string) {
ss := strutil.Split(rule, ";")
if len(ss) == 0 {
return
}

mp = make(map[string]string, len(flagTagKeys))
for _, s := range ss {
if strings.ContainsRune(s, '=') == false {
helper.Panicf("parse tag error on field '%s': item must match `KEY=VAL`", name)
}

kvNodes := strings.SplitN(s, "=", 2)
key, val := kvNodes[0], strings.TrimSpace(kvNodes[1])
if !flagTagKeys.Has(key) {
helper.Panicf("parse tag error on field '%s': invalid key name '%s'", name, key)
}

mp[key] = val
}
return
}

// ParseSimpleRule struct tag value use simple rule. each item split by ';'
//
// - format: "desc;required;default;shorts"
//
// eg:
//
// "int option message;required;i"
// "int option message;;a,b"
// "int option message;;a,b;23"
//
// returns field name:
//
// name
// desc
// shorts
// required
// default
func ParseSimpleRule(name, rule string) (mp map[string]string) {
ss := strutil.SplitNTrimmed(rule, ";", 4)
ln := len(ss)
if ln == 0 {
return
}

mp = make(map[string]string, ln)
mp["desc"] = ss[0]
if ln == 1 {
return
}

required := ss[1]
if required == "required" {
required = "true"
}

mp["required"] = required

// has shorts and default
if ln > 3 {
mp["default"], mp["shorts"] = ss[2], ss[3]
} else if ln > 2 {
mp["default"] = ss[2]
}
return
}

0 comments on commit 931b003

Please sign in to comment.