func (fs *Flags) checkShortNames(name string, nameLength int, shorts []string) []string {
	if len(shorts) == 0 {
		fs.names[name] = nameLength
		return shorts
	}
	if fs.shorts == nil {
		fs.shorts = map[string]string{}
		fs.name2shorts = map[string][]string{}
	}
	var fmtShorts []string
	for _, short := range shorts {
		short = strings.Trim(short, "- ")
		if short == "" {
			continue
		}
		char := short[0]
		short = string(char)
		if !strutil.IsAlphabet(char) {
			panicf("short name only allow: A-Za-z given: '%s'", short)
		}
		if name == short {
			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)
		}
		if n, ok := fs.shorts[short]; ok {
			panicf("short name '%s' has been used by option '%s'", short, n)
		}
		fmtShorts = append(fmtShorts, short)
		fs.shorts[short] = name
	}
	nameLength += 4 * len(fmtShorts)
	fs.existShort = true
	fs.names[name] = nameLength
	if fs.flagMaxLen < nameLength {
		fs.flagMaxLen = nameLength
	}
	fs.name2shorts[name] = fmtShorts
	return fmtShorts
}
check short names
package gcli
import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
	"unsafe"
	"github.com/gookit/color"
	"github.com/gookit/goutil"
	"github.com/gookit/goutil/arrutil"
	"github.com/gookit/goutil/strutil"
)
const (
	AlignLeft  = strutil.PosRight
	AlignRight = strutil.PosLeft
	defaultDesc = "No description"
	TagRuleNamed = 0
	TagRuleSimple = 1
)
var (
	FlagTagName = "flag"
	flagTagKeys = arrutil.Strings{"name", "shorts", "desc", "default", "required"}
)
type FlagsOption struct {
	WithoutType bool
	NameDescOL bool
	Alignment uint8
	TagName string
	TagRuleType uint8
}
type Flags struct {
	Desc string
	ExitFunc func(code int)
	opt *FlagsOption
	fSet *flag.FlagSet
	buf *bytes.Buffer
	out io.Writer
	names map[string]int
	metas map[string]*FlagMeta  
	shorts map[string]string
	name2shorts map[string][]string
	flagMaxLen int
	existShort bool
}
func newDefaultFlagOption() *FlagsOption {
	return &FlagsOption{
		Alignment: AlignRight,
		TagName:   FlagTagName,
	}
}
func NewFlags(nameWithDesc ...string) *Flags {
	fs := &Flags{
		out: os.Stdout,
		opt: newDefaultFlagOption(),
	}
	fs.ExitFunc = os.Exit
	fName := "gflag"
	if num := len(nameWithDesc); num > 0 {
		fName = nameWithDesc[0]
		if num > 1 {
			fs.Desc = nameWithDesc[1]
		}
	}
	fs.InitFlagSet(fName)
	return fs
}
var flagValueType = reflect.TypeOf(new(flag.Value)).Elem()
var errNotPtrValue = errors.New("must provide an ptr value")
var errNotAnStruct = errors.New("must provide an struct ptr")
var errTagRuleType = errors.New("invalid tag rule type on struct")
func (fs *Flags) FromStruct(ptr interface{}) error {
	v := reflect.ValueOf(ptr)
	if v.Kind() != reflect.Ptr {
		return errNotPtrValue
	}
	if !v.IsNil() {
		v = v.Elem()
	}
	t := v.Type()
	if t.Kind() != reflect.Struct {
		return errNotAnStruct
	}
	tagName := fs.opt.TagName
	if tagName == "" {
		tagName = FlagTagName
	}
	var mp map[string]string
	for i := 0; i < t.NumField(); i++ {
		sf := t.Field(i)
		name := sf.Name
		if name[0] >= 'a' && name[0] <= 'z' {
			continue
		}
		str := sf.Tag.Get(tagName)
		if str == "" {
			continue
		}
		fv := v.Field(i)
		ft := t.Field(i).Type
		if !fv.CanInterface() {
			continue
		}
		if ft.Kind() == reflect.Ptr {
			if fv.IsNil() {
				return fmt.Errorf("field: %s - nil pointer dereference", name)
			}
			ft = ft.Elem()
			fv = fv.Elem()
		}
		if fs.opt.TagRuleType == TagRuleNamed {
			mp = parseNamedRule(name, str)
		} else if fs.opt.TagRuleType == TagRuleSimple {
			mp = parseSimpleRule(name, str)
		} else {
			return errTagRuleType
		}
		defVal := mp["default"]
		shorts := splitShortcut(mp["shorts"])
		optName, has := mp["name"]
		if !has {  
			optName = strutil.SnakeCase(name, "-")
		}
		meta := newFlagMeta(optName, mp["desc"], defVal, shorts)
		if must, has := mp["required"]; has {
			meta.Required = strutil.MustBool(must)
		}
		if ft.Implements(flagValueType) {
			fs.Var(fv.Interface().(flag.Value), meta)
			continue
		}
		ptr := unsafe.Pointer(fv.UnsafeAddr())
		switch ft.Kind() {
		case reflect.Bool:
			fs.BoolVar((*bool)(ptr), meta)
		case reflect.Int:
			fs.IntVar((*int)(ptr), meta)
		case reflect.Int64:
			fs.Int64Var((*int64)(ptr), meta)
		case reflect.Uint:
			fs.UintVar((*uint)(ptr), meta)
		case reflect.Uint64:
			fs.Uint64Var((*uint64)(ptr), meta)
		case reflect.Float64:
			fs.Float64Var((*float64)(ptr), meta)
		case reflect.String:
			fs.StrVar((*string)(ptr), meta)
		default:
			return fmt.Errorf("field: %s - invalid type for binding flag", name)
		}
	}
	return nil
}
func (fs *Flags) SetOptions(opt *FlagsOption) {
	fs.opt = opt
}
func (fs *Flags) UseSimpleRule() *Flags {
	fs.opt.TagRuleType = TagRuleSimple
	return fs
}
func (fs *Flags) WithOptions(fns func(opt *FlagsOption)) *Flags {
	fns(fs.opt)
	return fs
}
func (fs *Flags) InitFlagSet(name string) {
	if fs.fSet != nil {
		return
	}
	fs.fSet = flag.NewFlagSet(name, flag.ContinueOnError)
	fs.fSet.SetOutput(ioutil.Discard)
	fs.fSet.Usage = func() {}
}
func (fs *Flags) Run(args []string) {
	if args == nil {
		args = os.Args
	}
	binFile, waitArgs := args[0], args[1:]
	fs.SetHelpRender(func() {
		if fs.Desc != "" {
			color.Infoln(fs.Desc)
		}
		color.Comment.Println("Usage:")
		color.Cyan.Println(" ", binFile, "[--OPTIONS...]\n")
		color.Comment.Println("Options:")
		fs.PrintHelpPanel()
	})
	if err := fs.Parse(waitArgs); err != nil {
		if err == flag.ErrHelp {
			fs.ExitFunc(0)
		} else {
			color.Errorf("flag parse error - %s", err.Error())
			fs.ExitFunc(2)
		}
	}
}
func (fs *Flags) Parse(args []string) (err error) {
	err = fs.fSet.Parse(args)
	if err != nil {
		return
	}
	for name, meta := range fs.metas {
		fItem := fs.fSet.Lookup(name)
		err = meta.Validate(fItem.Value.String())
		if err != nil {
			return err
		}
	}
	return
}
func (fs *Flags) RawArg(i int) string {
	return fs.fSet.Arg(i)
}
func (fs *Flags) RawArgs() []string {
	return fs.fSet.Args()
}
func (fs *Flags) FSetArgs() []string {
	return fs.fSet.Args()
}
func (fs *Flags) Bool(name, shorts string, defValue bool, desc string) *bool {
	meta := newFlagMeta(name, desc, defValue, splitShortcut(shorts))
	name = fs.checkFlagInfo(meta)
	p := fs.fSet.Bool(name, defValue, meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.BoolVar(p, s, defValue, "")  
	}
	return p
}
func (fs *Flags) BoolVar(p *bool, meta *FlagMeta) {
	fs.boolOpt(p, meta)
}
func (fs *Flags) BoolOpt(p *bool, name, shorts string, defValue bool, desc string) {
	fs.boolOpt(p, newFlagMeta(name, desc, defValue, splitShortcut(shorts)))
}
func (fs *Flags) boolOpt(p *bool, meta *FlagMeta) {
	defVal := meta.DValue().Bool()
	fmtName := fs.checkFlagInfo(meta)
	fs.fSet.BoolVar(p, fmtName, defVal, meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.BoolVar(p, s, defVal, "")  
	}
}
func (fs *Flags) Float64Var(p *float64, meta *FlagMeta) {
	fs.float64Opt(p, meta)
}
func (fs *Flags) Float64Opt(p *float64, name, shorts string, defValue float64, desc string) {
	fs.float64Opt(p, newFlagMeta(name, desc, defValue, splitShortcut(shorts)))
}
func (fs *Flags) float64Opt(p *float64, meta *FlagMeta) {
	defValue := meta.DValue().Float64()
	fmtName := fs.checkFlagInfo(meta)
	fs.fSet.Float64Var(p, fmtName, defValue, meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.Float64Var(p, s, defValue, "")  
	}
}
func (fs *Flags) Str(name, shorts string, defValue, desc string) *string {
	meta := newFlagMeta(name, desc, defValue, splitShortcut(shorts))
	name = fs.checkFlagInfo(meta)
	p := fs.fSet.String(name, defValue, meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.StringVar(p, s, defValue, "")  
	}
	return p
}
func (fs *Flags) StrVar(p *string, meta *FlagMeta) {
	fs.strOpt(p, meta)
}
func (fs *Flags) StrOpt(p *string, name, shorts, defValue, desc string) {
	fs.strOpt(p, newFlagMeta(name, desc, defValue, splitShortcut(shorts)))
}
func (fs *Flags) strOpt(p *string, meta *FlagMeta) {
	defValue := meta.DValue().String()
	fmtName := fs.checkFlagInfo(meta)
	fs.fSet.StringVar(p, fmtName, defValue, meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.StringVar(p, s, defValue, "")  
	}
}
func (fs *Flags) Int(name, shorts string, defValue int, desc string) *int {
	meta := newFlagMeta(name, desc, defValue, splitShortcut(shorts))
	name = fs.checkFlagInfo(meta)
	p := fs.fSet.Int(name, defValue, meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.IntVar(p, s, defValue, "")  
	}
	return p
}
func (fs *Flags) IntVar(p *int, meta *FlagMeta) {
	fs.intOpt(p, meta)
}
func (fs *Flags) IntOpt(p *int, name, shorts string, defValue int, desc string) {
	fs.intOpt(p, newFlagMeta(name, desc, defValue, splitShortcut(shorts)))
}
func (fs *Flags) intOpt(p *int, meta *FlagMeta) {
	defValue := meta.DValue().Int()
	fmtName := fs.checkFlagInfo(meta)
	fs.fSet.IntVar(p, fmtName, defValue, meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.IntVar(p, s, defValue, "")  
	}
}
func (fs *Flags) Int64(name, shorts string, defValue int64, desc string) *int64 {
	meta := newFlagMeta(name, desc, defValue, splitShortcut(shorts))
	name = fs.checkFlagInfo(meta)
	p := fs.fSet.Int64(name, defValue, meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.Int64Var(p, s, defValue, "")  
	}
	return p
}
func (fs *Flags) Int64Var(p *int64, meta *FlagMeta) {
	fs.int64Opt(p, meta)
}
func (fs *Flags) Int64Opt(p *int64, name, shorts string, defValue int64, desc string) {
	fs.int64Opt(p, newFlagMeta(name, desc, defValue, splitShortcut(shorts)))
}
func (fs *Flags) int64Opt(p *int64, meta *FlagMeta) {
	defValue := meta.DValue().Int64()
	fmtName := fs.checkFlagInfo(meta)
	fs.fSet.Int64Var(p, fmtName, defValue, meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.Int64Var(p, s, defValue, "")  
	}
}
func (fs *Flags) Uint(name, shorts string, defValue uint, desc string) *uint {
	meta := newFlagMeta(name, desc, defValue, splitShortcut(shorts))
	name = fs.checkFlagInfo(meta)
	p := fs.fSet.Uint(name, defValue, meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.UintVar(p, s, defValue, "")  
	}
	return p
}
func (fs *Flags) UintVar(p *uint, meta *FlagMeta) {
	fs.uintOpt(p, meta)
}
func (fs *Flags) UintOpt(p *uint, name, shorts string, defValue uint, desc string) {
	fs.uintOpt(p, newFlagMeta(name, desc, defValue, splitShortcut(shorts)))
}
func (fs *Flags) uintOpt(p *uint, meta *FlagMeta) {
	defValue := meta.DValue().Int()
	fmtName := fs.checkFlagInfo(meta)
	fs.fSet.UintVar(p, fmtName, uint(defValue), meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.UintVar(p, s, uint(defValue), "")  
	}
}
func (fs *Flags) Uint64(name, shorts string, defValue uint64, desc string) *uint64 {
	meta := newFlagMeta(name, desc, defValue, splitShortcut(shorts))
	name = fs.checkFlagInfo(meta)
	p := fs.fSet.Uint64(name, defValue, meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.Uint64Var(p, s, defValue, "")  
	}
	return p
}
func (fs *Flags) Uint64Var(p *uint64, meta *FlagMeta) {
	fs.uint64Opt(p, meta)
}
func (fs *Flags) Uint64Opt(p *uint64, name, shorts string, defValue uint64, desc string) {
	fs.uint64Opt(p, newFlagMeta(name, desc, defValue, splitShortcut(shorts)))
}
func (fs *Flags) uint64Opt(p *uint64, meta *FlagMeta) {
	defValue := meta.DValue().Int64()
	fmtName := fs.checkFlagInfo(meta)
	fs.fSet.Uint64Var(p, fmtName, uint64(defValue), meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.Uint64Var(p, s, uint64(defValue), "")  
	}
}
func (fs *Flags) Var(p flag.Value, meta *FlagMeta) {
	fs.varOpt(p, meta)
}
func (fs *Flags) VarOpt(p flag.Value, name, shorts, desc string) {
	fs.varOpt(p, newFlagMeta(name, desc, nil, splitShortcut(shorts)))
}
func (fs *Flags) varOpt(p flag.Value, meta *FlagMeta) {
	fmtName := fs.checkFlagInfo(meta)
	fs.fSet.Var(p, fmtName, meta.Desc)
	for _, s := range meta.Shorts {
		fs.fSet.Var(p, s, "")  
	}
}
func (fs *Flags) Required(names ...string) {
	for _, name := range names {
		meta, ok := fs.metas[name]
		if !ok {
			panicf("undefined option flag '%s'", name)
		}
		meta.Required = true
	}
}
func (fs *Flags) checkFlagInfo(meta *FlagMeta) string {
	if fs.names == nil {
		fs.names = map[string]int{}
		fs.metas = map[string]*FlagMeta{}
		fs.InitFlagSet("flags")
	}
	name := meta.goodName()
	if _, ok := fs.metas[name]; ok {
		panicf("redefined option flag '%s'", name)
	}
	nameLength := len(name)
	if nameLength == 1 {
		nameLength += 1  
		fs.existShort = true
	} else {
		nameLength += 2  
	}
	if !meta.Hidden && fs.flagMaxLen < nameLength {
		fs.flagMaxLen = nameLength
	}
	meta.Shorts = fs.checkShortNames(name, nameLength, meta.Shorts)
	fs.metas[name] = meta
	return name
}