diff --git a/flag.go b/flag.go index 1f2259f..66984bf 100644 --- a/flag.go +++ b/flag.go @@ -4,7 +4,9 @@ import ( "errors" "flag" "reflect" + "regexp" "strconv" + "strings" ) type FVSet struct { @@ -19,6 +21,56 @@ func (f *FVSet) Parse(args []string) error { func isPtr(v interface{}) bool { return reflect.TypeOf(v).Kind() == reflect.Ptr } +func (f *FVSet) Args() interface{} { + return f.flags.Args() +} + +func (f *FVSet) Usage() { + f.flags.Usage() +} + +type FVSets struct { + flagSets []*FVSet + subcommands []string +} + +func (f *FVSets) Add(v interface{}) error { + fv, err := NewFVSet(v) + if err != nil { + return err + } + fv.flags.Usage = func() { + f.Usage() + } + f.flagSets = append(f.flagSets, fv) + return nil +} +func (f *FVSets) Parse(args []string) error { + for _, v := range f.flagSets { + err := v.Parse(args) + if err != nil { + return err + } + } + return nil +} +func (f *FVSets) Usage() { + //for _, v := range f.flagSets { + // + //} +} + +func ToKebabCase(input string) string { + // 将字符串中的大写字母前插入连字符,并转换为小写 + re := regexp.MustCompile("([a-z])([A-Z])") + kebab := re.ReplaceAllString(input, "${1}-${2}") + kebab = strings.ToLower(kebab) + + // 去除字符串开头和结尾的连字符 + kebab = strings.Trim(kebab, "-") + + return kebab +} // NewFVSet 创建一个新的FVSet实例,用于处理命令行标志。 // 参数v是一个指向结构体的指针,该结构体的字段将被用作命令行标志。 @@ -51,23 +103,35 @@ func NewFVSet(v interface{}) (*FVSet, error) { if flaName == "" { flaName = stField.Name } + flaName = ToKebabCase(flaName) + flShort := stField.Tag.Get("flag_short") defVal := stField.Tag.Get("flag_default") usageStr := stField.Tag.Get("flag_usage") if usageStr == "" { usageStr = "Set " + flaName + "(default " + defVal + ")" } - + addr := field.Addr() if field.CanSet() { - switch expression := field.Interface().(type) { + switch field.Interface().(type) { case string: - flags.StringVar(&expression, flaName, defVal, usageStr) + flags.StringVar(addr.Interface().(*string), flaName, defVal, usageStr) + if flShort != "" { + flags.StringVar(addr.Interface().(*string), flShort, defVal, usageStr) + } break case int: v, _ := strconv.Atoi(defVal) - flags.IntVar(&expression, flaName, v, usageStr) + flags.IntVar(addr.Interface().(*int), flaName, v, usageStr) + if flShort != "" { + + flags.IntVar(addr.Interface().(*int), flShort, v, usageStr) + } break case bool: - flags.BoolVar(&expression, flaName, false, usageStr) + flags.BoolVar(addr.Interface().(*bool), flaName, false, usageStr) + if flShort != "" { + flags.BoolVar(addr.Interface().(*bool), flShort, false, usageStr) + } break default: diff --git a/flag_test.go b/flag_test.go new file mode 100644 index 0000000..7373c99 --- /dev/null +++ b/flag_test.go @@ -0,0 +1,29 @@ +package command + +import ( + "testing" +) + +func TestParseSingle(t *testing.T) { + type targs struct { + Test string `flag_default:"test" flag_usage:"this is test"` + TestBool bool `flag_short:"b"` + TestInt int + } + sargs := &targs{} + v, _ := NewFVSet(sargs) + v.Usage() + err := Parse([]string{"--test", "test", "--test-bool", "--test-int", "1"}, v) + if err != nil { + t.Error(err) + } + if sargs.Test != "test" { + t.Error("test failed") + } + if !sargs.TestBool { + t.Error("test failed") + } + if sargs.TestInt != 1 { + t.Error("test failed") + } +} diff --git a/usage.go b/usage.go new file mode 100644 index 0000000..43dbc62 --- /dev/null +++ b/usage.go @@ -0,0 +1,12 @@ +package command + +type FlagUsage struct { + LongName string + ShortName string + Usage string +} + +type Usage struct { + commands []string + flags []string +}