1package clihelpers 2// Copyright 2012 Jesse van den Kieboom. All rights reserved. 3// Use of this source code is governed by a BSD-style 4// license that can be found in the LICENSE file. 5// The source is taken from: https://raw.githubusercontent.com/jessevdk/go-flags/master/convert.go 6 7import ( 8 "fmt" 9 "reflect" 10 "strconv" 11 "strings" 12 "time" 13) 14 15// Marshaler is the interface implemented by types that can marshal themselves 16// to a string representation of the flag. 17type Marshaler interface { 18 // MarshalFlag marshals a flag value to its string representation. 19 MarshalFlag() (string, error) 20} 21 22// Unmarshaler is the interface implemented by types that can unmarshal a flag 23// argument to themselves. The provided value is directly passed from the 24// command line. 25type Unmarshaler interface { 26 // UnmarshalFlag unmarshals a string value representation to the flag 27 // value (which therefore needs to be a pointer receiver). 28 UnmarshalFlag(value string) error 29} 30 31func getBase(options reflect.StructTag, base int) (int, error) { 32 sbase := options.Get("base") 33 34 var err error 35 var ivbase int64 36 37 if sbase != "" { 38 ivbase, err = strconv.ParseInt(sbase, 10, 32) 39 base = int(ivbase) 40 } 41 42 return base, err 43} 44 45func convertMarshal(val reflect.Value) (bool, string, error) { 46 // Check first for the Marshaler interface 47 if val.Type().NumMethod() > 0 && val.CanInterface() { 48 if marshaler, ok := val.Interface().(Marshaler); ok { 49 ret, err := marshaler.MarshalFlag() 50 return true, ret, err 51 } 52 } 53 54 return false, "", nil 55} 56 57func convertToString(val reflect.Value, options reflect.StructTag) (string, error) { 58 if ok, ret, err := convertMarshal(val); ok { 59 return ret, err 60 } 61 62 tp := val.Type() 63 64 // Support for time.Duration 65 if tp == reflect.TypeOf((*time.Duration)(nil)).Elem() { 66 stringer := val.Interface().(fmt.Stringer) 67 return stringer.String(), nil 68 } 69 70 switch tp.Kind() { 71 case reflect.String: 72 return val.String(), nil 73 case reflect.Bool: 74 if val.Bool() { 75 return "true", nil 76 } 77 78 return "false", nil 79 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 80 base, err := getBase(options, 10) 81 82 if err != nil { 83 return "", err 84 } 85 86 return strconv.FormatInt(val.Int(), base), nil 87 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 88 base, err := getBase(options, 10) 89 90 if err != nil { 91 return "", err 92 } 93 94 return strconv.FormatUint(val.Uint(), base), nil 95 case reflect.Float32, reflect.Float64: 96 return strconv.FormatFloat(val.Float(), 'g', -1, tp.Bits()), nil 97 case reflect.Slice: 98 if val.Len() == 0 { 99 return "", nil 100 } 101 102 ret := "[" 103 104 for i := 0; i < val.Len(); i++ { 105 if i != 0 { 106 ret += ", " 107 } 108 109 item, err := convertToString(val.Index(i), options) 110 111 if err != nil { 112 return "", err 113 } 114 115 ret += item 116 } 117 118 return ret + "]", nil 119 case reflect.Map: 120 ret := "{" 121 122 for i, key := range val.MapKeys() { 123 if i != 0 { 124 ret += ", " 125 } 126 127 keyitem, err := convertToString(key, options) 128 129 if err != nil { 130 return "", err 131 } 132 133 item, err := convertToString(val.MapIndex(key), options) 134 135 if err != nil { 136 return "", err 137 } 138 139 ret += keyitem + ":" + item 140 } 141 142 return ret + "}", nil 143 case reflect.Ptr: 144 return convertToString(reflect.Indirect(val), options) 145 case reflect.Interface: 146 if !val.IsNil() { 147 return convertToString(val.Elem(), options) 148 } 149 } 150 151 return "", nil 152} 153 154func convertUnmarshal(val string, retval reflect.Value) (bool, error) { 155 if retval.Type().NumMethod() > 0 && retval.CanInterface() { 156 if unmarshaler, ok := retval.Interface().(Unmarshaler); ok { 157 return true, unmarshaler.UnmarshalFlag(val) 158 } 159 } 160 161 if retval.Type().Kind() != reflect.Ptr && retval.CanAddr() { 162 return convertUnmarshal(val, retval.Addr()) 163 } 164 165 if retval.Type().Kind() == reflect.Interface && !retval.IsNil() { 166 return convertUnmarshal(val, retval.Elem()) 167 } 168 169 return false, nil 170} 171 172func convert(val string, retval reflect.Value, options reflect.StructTag) error { 173 if ok, err := convertUnmarshal(val, retval); ok { 174 return err 175 } 176 177 tp := retval.Type() 178 179 // Support for time.Duration 180 if tp == reflect.TypeOf((*time.Duration)(nil)).Elem() { 181 parsed, err := time.ParseDuration(val) 182 183 if err != nil { 184 return err 185 } 186 187 retval.SetInt(int64(parsed)) 188 return nil 189 } 190 191 switch tp.Kind() { 192 case reflect.String: 193 retval.SetString(val) 194 case reflect.Bool: 195 if val == "" { 196 retval.SetBool(true) 197 } else { 198 b, err := strconv.ParseBool(val) 199 200 if err != nil { 201 return err 202 } 203 204 retval.SetBool(b) 205 } 206 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 207 base, err := getBase(options, 10) 208 209 if err != nil { 210 return err 211 } 212 213 parsed, err := strconv.ParseInt(val, base, tp.Bits()) 214 215 if err != nil { 216 return err 217 } 218 219 retval.SetInt(parsed) 220 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 221 base, err := getBase(options, 10) 222 223 if err != nil { 224 return err 225 } 226 227 parsed, err := strconv.ParseUint(val, base, tp.Bits()) 228 229 if err != nil { 230 return err 231 } 232 233 retval.SetUint(parsed) 234 case reflect.Float32, reflect.Float64: 235 parsed, err := strconv.ParseFloat(val, tp.Bits()) 236 237 if err != nil { 238 return err 239 } 240 241 retval.SetFloat(parsed) 242 case reflect.Slice: 243 elemtp := tp.Elem() 244 245 elemvalptr := reflect.New(elemtp) 246 elemval := reflect.Indirect(elemvalptr) 247 248 if err := convert(val, elemval, options); err != nil { 249 return err 250 } 251 252 retval.Set(reflect.Append(retval, elemval)) 253 case reflect.Map: 254 parts := strings.SplitN(val, ":", 2) 255 256 key := parts[0] 257 var value string 258 259 if len(parts) == 2 { 260 value = parts[1] 261 } 262 263 keytp := tp.Key() 264 keyval := reflect.New(keytp) 265 266 if err := convert(key, keyval, options); err != nil { 267 return err 268 } 269 270 valuetp := tp.Elem() 271 valueval := reflect.New(valuetp) 272 273 if err := convert(value, valueval, options); err != nil { 274 return err 275 } 276 277 if retval.IsNil() { 278 retval.Set(reflect.MakeMap(tp)) 279 } 280 281 retval.SetMapIndex(reflect.Indirect(keyval), reflect.Indirect(valueval)) 282 case reflect.Ptr: 283 if retval.IsNil() { 284 retval.Set(reflect.New(retval.Type().Elem())) 285 } 286 287 return convert(val, reflect.Indirect(retval), options) 288 case reflect.Interface: 289 if !retval.IsNil() { 290 return convert(val, retval.Elem(), options) 291 } 292 } 293 294 return nil 295} 296 297func isPrint(s string) bool { 298 for _, c := range s { 299 if !strconv.IsPrint(c) { 300 return false 301 } 302 } 303 304 return true 305} 306 307func quoteIfNeeded(s string) string { 308 if !isPrint(s) { 309 return strconv.Quote(s) 310 } 311 312 return s 313} 314 315func quoteIfNeededV(s []string) []string { 316 ret := make([]string, len(s)) 317 318 for i, v := range s { 319 ret[i] = quoteIfNeeded(v) 320 } 321 322 return ret 323} 324 325func quoteV(s []string) []string { 326 ret := make([]string, len(s)) 327 328 for i, v := range s { 329 ret[i] = strconv.Quote(v) 330 } 331 332 return ret 333} 334 335func unquoteIfPossible(s string) (string, error) { 336 if len(s) == 0 || s[0] != '"' { 337 return s, nil 338 } 339 340 return strconv.Unquote(s) 341} 342 343func wrapText(s string, l int, prefix string) string { 344 // Basic text wrapping of s at spaces to fit in l 345 var ret string 346 347 s = strings.TrimSpace(s) 348 349 for len(s) > l { 350 // Try to split on space 351 suffix := "" 352 353 pos := strings.LastIndex(s[:l], " ") 354 355 if pos < 0 { 356 pos = l - 1 357 suffix = "-\n" 358 } 359 360 if len(ret) != 0 { 361 ret += "\n" + prefix 362 } 363 364 ret += strings.TrimSpace(s[:pos]) + suffix 365 s = strings.TrimSpace(s[pos:]) 366 } 367 368 if len(s) > 0 { 369 if len(ret) != 0 { 370 ret += "\n" + prefix 371 } 372 373 return ret + s 374 } 375 376 return ret 377} 378