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 // ---------------------------------------------------------------------------