github.com/qiniu/dyn@v1.3.0/cmdargs/parser.go (about)

     1  package cmdargs
     2  
     3  import (
     4  	"errors"
     5  	"flag"
     6  	"reflect"
     7  	"strconv"
     8  	"strings"
     9  	"syscall"
    10  
    11  	"github.com/qiniu/dyn/cmdarg"
    12  	"github.com/qiniu/x/jsonutil"
    13  	"github.com/qiniu/x/log"
    14  
    15  	. "github.com/qiniu/dyn/vars"
    16  )
    17  
    18  var (
    19  	ErrParamsNotEnough     = errors.New("params not enough")
    20  	ErrTooMuchParams       = errors.New("too much params")
    21  	ErrUnsupportedFlagType = errors.New("unsupported flag type")
    22  	ErrUnsupportedArgType  = errors.New("unsupported argument type")
    23  )
    24  
    25  // ---------------------------------------------------------------------------
    26  
    27  func hasOption(tag string, opt string) bool {
    28  
    29  	for i := 0; i < len(tag); i++ {
    30  		switch tag[i] {
    31  		case ',':
    32  			if strings.HasPrefix(tag[i+1:], opt) {
    33  				tagLeft := tag[i+1+len(opt):]
    34  				if tagLeft == "" || tagLeft[0] == ',' || tagLeft[0] == ' ' {
    35  					return true
    36  				}
    37  			}
    38  		case ' ':
    39  			return false
    40  		}
    41  	}
    42  	return false
    43  }
    44  
    45  func parseFlagArg(fv reflect.Value, f *flag.FlagSet, tag string) (err error) {
    46  
    47  	n := 0
    48  	for n < len(tag) {
    49  		if tag[n] == ',' || tag[n] == ' ' {
    50  			break
    51  		}
    52  		n++
    53  	}
    54  
    55  	name := tag[:n]
    56  	usage := ""
    57  	pos := strings.Index(tag[n:], " - ")
    58  	if pos >= 0 {
    59  		usage = tag[pos+3:]
    60  	}
    61  
    62  	switch fv.Kind() {
    63  	case reflect.Ptr:
    64  		switch fv.Elem().Kind() {
    65  		case reflect.Bool:
    66  			fv.Set(reflect.ValueOf(f.Bool(name, false, usage)))
    67  		case reflect.Int:
    68  			fv.Set(reflect.ValueOf(f.Int(name, 0, usage)))
    69  		case reflect.Uint:
    70  			fv.Set(reflect.ValueOf(f.Uint(name, 0, usage)))
    71  		case reflect.Uint64:
    72  			fv.Set(reflect.ValueOf(f.Uint64(name, 0, usage)))
    73  		default:
    74  			return ErrUnsupportedFlagType
    75  		}
    76  	case reflect.Bool:
    77  		f.BoolVar(fv.Addr().Interface().(*bool), name, false, usage)
    78  	case reflect.Int:
    79  		f.IntVar(fv.Addr().Interface().(*int), name, 0, usage)
    80  	case reflect.Uint:
    81  		f.UintVar(fv.Addr().Interface().(*uint), name, 0, usage)
    82  	case reflect.Uint64:
    83  		f.Uint64Var(fv.Addr().Interface().(*uint64), name, 0, usage)
    84  	default:
    85  		return ErrUnsupportedFlagType
    86  	}
    87  	return nil
    88  }
    89  
    90  type parseArgOpts struct {
    91  	ft       string
    92  	keep     bool
    93  	optional bool
    94  }
    95  
    96  func getFmttype(ft string, ftDefault int) int {
    97  
    98  	switch ft {
    99  	case "form":
   100  		return Fmttype_Form
   101  	case "text":
   102  		return Fmttype_Text
   103  	case "json":
   104  		return Fmttype_Json
   105  	}
   106  	return ftDefault
   107  }
   108  
   109  func parseArgTag(tag string) (opts parseArgOpts, err error) {
   110  
   111  	pos := strings.Index(tag, " - ")
   112  	if pos >= 0 {
   113  		tag = tag[:pos]
   114  	}
   115  
   116  	parts := strings.Split(tag, ",")
   117  	for i := 1; i < len(parts); i++ {
   118  		switch parts[i] {
   119  		case "keep":
   120  			opts.keep = true
   121  		case "form", "text", "json":
   122  			opts.ft = parts[i]
   123  		case "opt":
   124  			opts.optional = true
   125  		default:
   126  			err = errors.New("Unknown tag option: " + parts[i])
   127  			return
   128  		}
   129  	}
   130  	return
   131  }
   132  
   133  func parseArg(
   134  	ctx *Context, fv reflect.Value, arg string, opts parseArgOpts) (err error) {
   135  
   136  	kind := fv.Kind()
   137  	switch kind {
   138  	case reflect.String:
   139  		if opts.keep { // 保留 $(var) 不要自动展开
   140  			fv.SetString(arg)
   141  			return
   142  		}
   143  		arg, err = ctx.SubstText(arg, getFmttype(opts.ft, Fmttype_Text))
   144  		if err != nil {
   145  			return
   146  		}
   147  		fv.SetString(arg)
   148  
   149  	case reflect.Interface:
   150  		argObj, err1 := cmdarg.Unmarshal(arg)
   151  		if err1 != nil {
   152  			return err1
   153  		}
   154  		if opts.keep { // 保留 $(var) 不要做 Subst
   155  			fv.Set(reflect.ValueOf(argObj))
   156  			return
   157  		}
   158  		argObj, err2 := ctx.Subst(argObj, getFmttype(opts.ft, Fmttype_Text))
   159  		if err2 != nil {
   160  			return err2
   161  		}
   162  		fv.Set(reflect.ValueOf(argObj))
   163  
   164  	default:
   165  		if kind >= reflect.Int && kind <= reflect.Int64 {
   166  			arg, err = ctx.SubstText(arg, Fmttype_Text)
   167  			if err != nil {
   168  				return
   169  			}
   170  			intVal, err2 := strconv.ParseInt(arg, 10, 64)
   171  			if err2 != nil {
   172  				return err2
   173  			}
   174  			fv.SetInt(intVal)
   175  			return nil
   176  		}
   177  		if kind >= reflect.Uint && kind <= reflect.Uintptr {
   178  			arg, err = ctx.SubstText(arg, Fmttype_Text)
   179  			if err != nil {
   180  				return
   181  			}
   182  			uintVal, err2 := strconv.ParseUint(arg, 10, 64)
   183  			if err2 != nil {
   184  				return err2
   185  			}
   186  			fv.SetUint(uintVal)
   187  			return nil
   188  		}
   189  		arg, err = ctx.SubstText(arg, getFmttype(opts.ft, Fmttype_Json))
   190  		if err != nil {
   191  			return
   192  		}
   193  		err = jsonutil.Unmarshal(arg, fv.Addr().Interface())
   194  		if err != nil {
   195  			log.Debug("parseCmdArgs failed:", err, "arg:", arg)
   196  			return
   197  		}
   198  	}
   199  	return nil
   200  }
   201  
   202  func parseVargs(
   203  	ctx *Context, fv reflect.Value, args []string, opts parseArgOpts) (err error) {
   204  
   205  	sliceType := fv.Type()
   206  	n := len(args)
   207  	sliceValue := reflect.MakeSlice(sliceType, n, n)
   208  	for i, arg := range args {
   209  		err = parseArg(ctx, sliceValue.Index(i), arg, opts)
   210  		if err != nil {
   211  			return
   212  		}
   213  	}
   214  	fv.Set(sliceValue)
   215  	return
   216  }
   217  
   218  func parseStructArgs(
   219  	ctx *Context, strucType reflect.Type, cmd []string) (args reflect.Value, err error) {
   220  
   221  	nField := strucType.NumField()
   222  
   223  	hasFlag := false
   224  	for i := 0; i < nField; i++ {
   225  		sf := strucType.Field(i)
   226  		if strings.HasPrefix(string(sf.Tag), "flag:") {
   227  			hasFlag = true
   228  			break
   229  		}
   230  	}
   231  
   232  	args = reflect.New(strucType)
   233  	argsRef := args.Elem()
   234  
   235  	if hasFlag {
   236  		f := flag.NewFlagSet(cmd[0], 0)
   237  		for i := 0; i < nField; i++ {
   238  			sf := strucType.Field(i)
   239  			if strings.HasPrefix(string(sf.Tag), "f") {
   240  				err = parseFlagArg(argsRef.Field(i), f, sf.Tag.Get("flag"))
   241  				if err != nil {
   242  					return
   243  				}
   244  			}
   245  		}
   246  		err = f.Parse(cmd[1:])
   247  		if err != nil {
   248  			return
   249  		}
   250  		cmd = f.Args()
   251  	} else {
   252  		cmd = cmd[1:]
   253  	}
   254  
   255  	icmd := 0
   256  	for i := 0; i < nField; i++ {
   257  		sf := strucType.Field(i)
   258  		if strings.HasPrefix(string(sf.Tag), "arg:") {
   259  			tag := sf.Tag.Get("arg")
   260  			opts, err2 := parseArgTag(tag)
   261  			if err2 != nil {
   262  				err = err2
   263  				return
   264  			}
   265  			fv := argsRef.Field(i)
   266  			if fv.Kind() == reflect.Slice { // 不定参数
   267  				err = parseVargs(ctx, fv, cmd[icmd:], opts)
   268  				return
   269  			}
   270  			if icmd >= len(cmd) {
   271  				if opts.optional { // 可选参数
   272  					return
   273  				}
   274  				err = ErrParamsNotEnough
   275  				return
   276  			}
   277  			err = parseArg(ctx, fv, cmd[icmd], opts)
   278  			if err != nil {
   279  				return
   280  			}
   281  			icmd++
   282  		}
   283  	}
   284  	if icmd != len(cmd) {
   285  		err = ErrTooMuchParams
   286  	}
   287  	return
   288  }
   289  
   290  func Parse(
   291  	ctx *Context, argsType reflect.Type, cmd []string) (args reflect.Value, err error) {
   292  
   293  	switch argsType.Kind() {
   294  	case reflect.Ptr: // may be args *xxxArgs
   295  		strucType := argsType.Elem()
   296  		if strucType.Kind() == reflect.Struct {
   297  			return parseStructArgs(ctx, strucType, cmd)
   298  		}
   299  	case reflect.Slice: // may be args []string
   300  		if argsType.Elem().Kind() == reflect.String {
   301  			return reflect.ValueOf(cmd), nil
   302  		}
   303  	}
   304  	err = syscall.EINVAL
   305  	return
   306  }
   307  
   308  // ---------------------------------------------------------------------------