From ff4c06dd0a9e95f6ee2b4114e29f278d0eec1bcf Mon Sep 17 00:00:00 2001 From: kingecg Date: Tue, 22 Oct 2024 01:21:09 +0800 Subject: [PATCH] add code --- flag.go | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 flag.go diff --git a/flag.go b/flag.go new file mode 100644 index 0000000..1f2259f --- /dev/null +++ b/flag.go @@ -0,0 +1,110 @@ +package command + +import ( + "errors" + "flag" + "reflect" + "strconv" +) + +type FVSet struct { + Name string + Value *any + flags flag.FlagSet +} + +func (f *FVSet) Parse(args []string) error { + return f.flags.Parse(args) +} +func isPtr(v interface{}) bool { + return reflect.TypeOf(v).Kind() == reflect.Ptr +} + +// NewFVSet 创建一个新的FVSet实例,用于处理命令行标志。 +// 参数v是一个指向结构体的指针,该结构体的字段将被用作命令行标志。 +// 返回值是一个指向FVSet的指针和一个错误对象,如果操作成功,错误对象为nil。 +// 如果v不是指向结构体的指针,函数将返回错误。 +func NewFVSet(v interface{}) (*FVSet, error) { + // 判断v是不是指针 + vv := reflect.ValueOf(v) + if !isPtr(v) { + return nil, errors.New("v must be a pointer") + } + // 判断指针指向的值是不是结构体 + vvv := vv.Elem() + if vvv.Kind() != reflect.Struct { + return nil, errors.New("v must be a pointer to a struct") + } + flags := flag.NewFlagSet(vvv.Type().Name(), flag.ContinueOnError) + name := vvv.Type().Name() + // 遍历结构体字段 + for i := 0; i < vvv.NumField(); i++ { + //skip private field + stField := vvv.Type().Field(i) + if stField.PkgPath != "" { + continue + } + // 获取字段名 + field := vvv.Field(i) + nameTag := stField.Tag.Get("flag_name") + flaName := nameTag + if flaName == "" { + flaName = stField.Name + } + defVal := stField.Tag.Get("flag_default") + usageStr := stField.Tag.Get("flag_usage") + if usageStr == "" { + usageStr = "Set " + flaName + "(default " + defVal + ")" + } + + if field.CanSet() { + switch expression := field.Interface().(type) { + case string: + flags.StringVar(&expression, flaName, defVal, usageStr) + break + case int: + v, _ := strconv.Atoi(defVal) + flags.IntVar(&expression, flaName, v, usageStr) + break + case bool: + flags.BoolVar(&expression, flaName, false, usageStr) + break + + default: + return nil, errors.New("unsupported type") + } + } + } + return &FVSet{ + Name: name, + Value: &v, + flags: *flags, + }, nil +} + +// Parse is used to parse command line arguments based on one or more flag sets. +// args represents the list of command line arguments. +// flagSet is a variable-length parameter that contains the flag sets to be parsed. +// If no flag set is provided, it throws a panic exception. +// If only one flag set is provided, it directly parses the arguments with that flag set. +// If multiple flag sets are provided, it attempts to match and parse based on the first argument in args. +// If no matching flag set is found, it returns an error. +func Parse(args []string, flagSet ...*FVSet) error { + // Check if the flag set is missing, if so, throw a panic exception. + if len(flagSet) == 0 { + panic("flag set missing") + } + // If only one flag set is provided, directly parse the arguments with that flag set. + if len(flagSet) == 1 { + return flagSet[0].Parse(args) + } + // Iterate through the flag sets to find a match for parsing. + for _, f := range flagSet { + // If a matching flag set is found, parse the remaining arguments with that flag set. + if f.Name == args[0] { + return f.Parse(args[1:]) + } + } + // If no matching flag set is found, return an error. + return errors.New("flag set not found") +}