Upstream urfave/cli state of the touched files before 3510d4c9d9
This commit is contained in:
commit
8a9f09fc09
2 files changed
+1246
No files matched your search
@@ -0,0 +1,517 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/gommon/color"
|
||||
"github.com/mkideal/expr"
|
||||
)
|
||||
|
||||
type flag struct {
|
||||
field reflect.StructField
|
||||
value reflect.Value
|
||||
|
||||
// isAssigned indicates whether the flag is set(contains default value)
|
||||
isAssigned bool
|
||||
|
||||
// isSet indicates whether the flag is set
|
||||
isSet bool
|
||||
|
||||
// tag properties
|
||||
tag tagProperty
|
||||
|
||||
// actual flag name
|
||||
actualFlagName string
|
||||
|
||||
isNeedDelaySet bool
|
||||
|
||||
// last value for need delay set
|
||||
// flag maybe assigned too many times, like:
|
||||
// -f xx -f yy -f zz
|
||||
// `zz` is the last value
|
||||
lastValue string
|
||||
}
|
||||
|
||||
func newFlag(field reflect.StructField, value reflect.Value, tag *tagProperty, clr color.Color, dontSetValue bool) (fl *flag, err error) {
|
||||
fl = &flag{field: field, value: value}
|
||||
if !fl.value.CanSet() {
|
||||
return nil, fmt.Errorf("field %s can not set", clr.Bold(fl.field.Name))
|
||||
}
|
||||
fl.tag = *tag
|
||||
if fl.isPtr() && fl.value.IsNil() {
|
||||
fl.value.Set(reflect.New(fl.field.Type.Elem()))
|
||||
}
|
||||
isSliceDecoder := fl.value.Type().Implements(reflect.TypeOf((*SliceDecoder)(nil)).Elem())
|
||||
if !isSliceDecoder && fl.value.CanAddr() {
|
||||
isSliceDecoder = fl.value.Addr().Type().Implements(reflect.TypeOf((*SliceDecoder)(nil)).Elem())
|
||||
}
|
||||
fl.isNeedDelaySet = fl.tag.parserCreator != nil ||
|
||||
(fl.field.Type.Kind() != reflect.Slice && fl.field.Type.Kind() != reflect.Map && !isSliceDecoder)
|
||||
err = fl.init(clr, dontSetValue)
|
||||
return
|
||||
}
|
||||
|
||||
func (fl *flag) init(clr color.Color, dontSetValue bool) error {
|
||||
var (
|
||||
isNumber = fl.isInteger() || fl.isFloat()
|
||||
isDecoder = fl.value.Type().Implements(reflect.TypeOf((*Decoder)(nil)).Elem())
|
||||
dft string
|
||||
err error
|
||||
)
|
||||
if !isDecoder && fl.value.CanAddr() {
|
||||
isDecoder = fl.value.Addr().Type().Implements(reflect.TypeOf((*Decoder)(nil)).Elem())
|
||||
}
|
||||
dft, err = parseExpression(fl.tag.dft, isNumber)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isNumber && !isDecoder {
|
||||
v, err := expr.Eval(dft, nil, nil)
|
||||
if err == nil {
|
||||
if fl.isInteger() {
|
||||
dft = fmt.Sprintf("%d", v.Int())
|
||||
} else if fl.isFloat() {
|
||||
dft = fmt.Sprintf("%f", v.Float())
|
||||
}
|
||||
}
|
||||
}
|
||||
if !dontSetValue && fl.tag.dft != "" && dft != "" {
|
||||
if fl.isPtr() || isDecoder || isEmpty(fl.value) {
|
||||
return fl.setDefault(dft, clr)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isEmpty(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isWordByte(b byte) bool {
|
||||
return (b >= 'a' && b <= 'z') ||
|
||||
(b >= 'A' && b <= 'Z') ||
|
||||
(b >= '0' && b <= '9') ||
|
||||
b == '_'
|
||||
}
|
||||
|
||||
const (
|
||||
builtinVar_EXEC_PATH = "__EXEC_PATH"
|
||||
builtinVar_EXEC_FILENAME = "__EXEC_FILENAME"
|
||||
)
|
||||
|
||||
func parseExpression(s string, isNumber bool) (string, error) {
|
||||
const escapeByte = '$'
|
||||
var (
|
||||
src = []byte(s)
|
||||
escaping = false
|
||||
exprBuf bytes.Buffer
|
||||
envvarBuf bytes.Buffer
|
||||
)
|
||||
writeEnv := func(envName string) error {
|
||||
if envName == "" {
|
||||
return fmt.Errorf("unexpected end after %v", escapeByte)
|
||||
}
|
||||
var value string
|
||||
switch envName {
|
||||
case builtinVar_EXEC_PATH:
|
||||
filename, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value = filepath.Dir(filename)
|
||||
case builtinVar_EXEC_FILENAME:
|
||||
filename, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
value = strings.TrimSuffix(filename, ".exe")
|
||||
} else {
|
||||
value = filename
|
||||
}
|
||||
default:
|
||||
value = os.Getenv(envName)
|
||||
if value == "" && isNumber {
|
||||
value = "0"
|
||||
}
|
||||
}
|
||||
exprBuf.WriteString(value)
|
||||
return nil
|
||||
}
|
||||
for i, b := range src {
|
||||
if b == escapeByte {
|
||||
if escaping && envvarBuf.Len() == 0 {
|
||||
exprBuf.WriteByte(b)
|
||||
escaping = false
|
||||
} else {
|
||||
escaping = true
|
||||
if i+1 == len(src) {
|
||||
return "", fmt.Errorf("unexpected end after %v", escapeByte)
|
||||
}
|
||||
envvarBuf.Reset()
|
||||
}
|
||||
continue
|
||||
}
|
||||
if escaping {
|
||||
if isWordByte(b) {
|
||||
envvarBuf.WriteByte(b)
|
||||
if i+1 == len(src) {
|
||||
if err := writeEnv(envvarBuf.String()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := writeEnv(envvarBuf.String()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
exprBuf.WriteByte(b)
|
||||
envvarBuf.Reset()
|
||||
escaping = false
|
||||
}
|
||||
} else {
|
||||
exprBuf.WriteByte(b)
|
||||
}
|
||||
}
|
||||
return exprBuf.String(), nil
|
||||
}
|
||||
|
||||
func (fl *flag) name() string {
|
||||
if fl.actualFlagName != "" {
|
||||
return fl.actualFlagName
|
||||
}
|
||||
if len(fl.tag.longNames) > 0 {
|
||||
return fl.tag.longNames[0]
|
||||
}
|
||||
if len(fl.tag.shortNames) > 0 {
|
||||
return fl.tag.shortNames[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (fl *flag) isBoolean() bool {
|
||||
return fl.field.Type.Kind() == reflect.Bool
|
||||
}
|
||||
|
||||
func (fl *flag) isInteger() bool {
|
||||
switch fl.field.Type.Kind() {
|
||||
case reflect.Int,
|
||||
reflect.Int8,
|
||||
reflect.Int16,
|
||||
reflect.Int32,
|
||||
reflect.Int64,
|
||||
reflect.Uint,
|
||||
reflect.Uint8,
|
||||
reflect.Uint16,
|
||||
reflect.Uint32,
|
||||
reflect.Uint64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (fl *flag) isSlice() bool {
|
||||
return fl.field.Type.Kind() == reflect.Slice
|
||||
}
|
||||
|
||||
func (fl *flag) isMap() bool {
|
||||
return fl.field.Type.Kind() == reflect.Map
|
||||
}
|
||||
|
||||
func (fl *flag) isFloat() bool {
|
||||
kind := fl.field.Type.Kind()
|
||||
return kind == reflect.Float32 || kind == reflect.Float64
|
||||
}
|
||||
|
||||
func (fl *flag) isString() bool {
|
||||
return fl.field.Type.Kind() == reflect.String
|
||||
}
|
||||
|
||||
func (fl *flag) isPtr() bool {
|
||||
return fl.field.Type.Kind() == reflect.Ptr
|
||||
}
|
||||
|
||||
func (fl *flag) getBool() bool {
|
||||
if !fl.isBoolean() {
|
||||
return false
|
||||
}
|
||||
return fl.value.Bool()
|
||||
}
|
||||
|
||||
func (fl *flag) setDefault(s string, clr color.Color) error {
|
||||
fl.isAssigned = true
|
||||
if fl.isNeedDelaySet {
|
||||
fl.lastValue = s
|
||||
return nil
|
||||
}
|
||||
return setWithProperType(fl, fl.field.Type, fl.value, s, clr, false)
|
||||
}
|
||||
|
||||
func (fl *flag) set(actualFlagName, s string, clr color.Color) error {
|
||||
fl.isSet = true
|
||||
fl.isAssigned = true
|
||||
fl.actualFlagName = actualFlagName
|
||||
if fl.isNeedDelaySet {
|
||||
fl.lastValue = s
|
||||
return nil
|
||||
}
|
||||
return setWithProperType(fl, fl.field.Type, fl.value, s, clr, false)
|
||||
}
|
||||
|
||||
func (fl *flag) counterIncr(s string, clr color.Color) error {
|
||||
return setWithProperType(fl, fl.field.Type, fl.value, s, clr, false)
|
||||
}
|
||||
|
||||
func (fl *flag) isCounter() bool {
|
||||
if decoder := tryGetDecoder(fl.value.Type().Kind(), fl.value); decoder != nil {
|
||||
if _, ok := decoder.(CounterDecoder); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (fl *flag) setWithNoDelay(actualFlagName, s string, clr color.Color) error {
|
||||
fl.isSet = true
|
||||
fl.isAssigned = true
|
||||
fl.actualFlagName = actualFlagName
|
||||
return setWithProperType(fl, fl.field.Type, fl.value, s, clr, false)
|
||||
}
|
||||
|
||||
func tryGetDecoder(kind reflect.Kind, val reflect.Value) Decoder {
|
||||
if val.CanInterface() {
|
||||
var addrVal = val
|
||||
if kind != reflect.Ptr && val.CanAddr() {
|
||||
addrVal = val.Addr()
|
||||
}
|
||||
// try Decoder
|
||||
if addrVal.CanInterface() {
|
||||
if i := addrVal.Interface(); i != nil {
|
||||
if decoder, ok := i.(Decoder); ok {
|
||||
return decoder
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setWithProperType(fl *flag, typ reflect.Type, val reflect.Value, s string, clr color.Color, isSubField bool) error {
|
||||
kind := typ.Kind()
|
||||
|
||||
// try parser first of all
|
||||
if fl.tag.parserCreator != nil && val.CanInterface() {
|
||||
if kind != reflect.Ptr && val.CanAddr() {
|
||||
val = val.Addr()
|
||||
}
|
||||
return fl.tag.parserCreator(val.Interface()).Parse(s)
|
||||
}
|
||||
|
||||
if decoder := tryGetDecoder(kind, val); decoder != nil {
|
||||
return decoder.Decode(s)
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
if v, err := getBool(s, clr); err == nil {
|
||||
val.SetBool(v)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
case reflect.String:
|
||||
val.SetString(s)
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if v, err := getInt(s, clr); err == nil {
|
||||
if minmaxIntCheck(kind, v) {
|
||||
val.SetInt(v)
|
||||
} else {
|
||||
return errors.New(clr.Red("value overflow"))
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
if v, err := getUint(s, clr); err == nil {
|
||||
if minmaxUintCheck(kind, v) {
|
||||
val.SetUint(uint64(v))
|
||||
} else {
|
||||
return errors.New(clr.Red("value overflow"))
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if v, err := getFloat(s, clr); err == nil {
|
||||
if minmaxFloatCheck(kind, v) {
|
||||
val.SetFloat(float64(v))
|
||||
} else {
|
||||
return errors.New(clr.Red("value overflow"))
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
if isSubField {
|
||||
return fmt.Errorf("unsupported type %s as a sub field", kind.String())
|
||||
}
|
||||
sliceOf := typ.Elem()
|
||||
if val.IsNil() {
|
||||
slice := reflect.MakeSlice(typ, 0, 4)
|
||||
val.Set(slice)
|
||||
}
|
||||
index := val.Len()
|
||||
sliceCap := val.Cap()
|
||||
if index+1 <= sliceCap {
|
||||
val.SetLen(index + 1)
|
||||
} else {
|
||||
slice := reflect.MakeSlice(typ, index+1, index+sliceCap/2+1)
|
||||
for k := 0; k < index; k++ {
|
||||
slice.Index(k).Set(val.Index(k))
|
||||
}
|
||||
val.Set(slice)
|
||||
}
|
||||
return setWithProperType(fl, sliceOf, val.Index(index), s, clr, true)
|
||||
|
||||
case reflect.Map:
|
||||
if isSubField {
|
||||
return fmt.Errorf("unsupported type %s as a sub field", kind.String())
|
||||
}
|
||||
keyString, valString, err := splitKeyVal(s, fl.tag.sep)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keyType := typ.Key()
|
||||
valType := typ.Elem()
|
||||
if val.IsNil() {
|
||||
val.Set(reflect.MakeMap(typ))
|
||||
}
|
||||
k, v := reflect.New(keyType), reflect.New(valType)
|
||||
if err := setWithProperType(fl, keyType, k.Elem(), keyString, clr, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := setWithProperType(fl, valType, v.Elem(), valString, clr, true); err != nil {
|
||||
return err
|
||||
}
|
||||
val.SetMapIndex(k.Elem(), v.Elem())
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported type: %s", kind.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func splitKeyVal(s, sep string) (key, val string, err error) {
|
||||
if s == "" {
|
||||
err = fmt.Errorf("empty key,val pair")
|
||||
return
|
||||
}
|
||||
index := strings.Index(s, sep)
|
||||
if index == -1 {
|
||||
return s, "", nil
|
||||
}
|
||||
return s[:index], s[index+1:], nil
|
||||
}
|
||||
|
||||
func minmaxIntCheck(kind reflect.Kind, v int64) bool {
|
||||
switch kind {
|
||||
case reflect.Int, reflect.Int64:
|
||||
return v >= int64(math.MinInt64) && v <= int64(math.MaxInt64)
|
||||
case reflect.Int8:
|
||||
return v >= int64(math.MinInt8) && v <= int64(math.MaxInt8)
|
||||
case reflect.Int16:
|
||||
return v >= int64(math.MinInt16) && v <= int64(math.MaxInt16)
|
||||
case reflect.Int32:
|
||||
return v >= int64(math.MinInt32) && v <= int64(math.MaxInt32)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func minmaxUintCheck(kind reflect.Kind, v uint64) bool {
|
||||
switch kind {
|
||||
case reflect.Uint, reflect.Uint64:
|
||||
return v <= math.MaxUint64
|
||||
case reflect.Uint8:
|
||||
return v <= math.MaxUint8
|
||||
case reflect.Uint16:
|
||||
return v <= math.MaxUint16
|
||||
case reflect.Uint32:
|
||||
return v <= math.MaxUint32
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func minmaxFloatCheck(kind reflect.Kind, v float64) bool {
|
||||
switch kind {
|
||||
case reflect.Float32:
|
||||
return v >= -float64(math.MaxFloat32) && v <= float64(math.MaxFloat32)
|
||||
case reflect.Float64:
|
||||
return v >= -float64(math.MaxFloat64) && v <= float64(math.MaxFloat64)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getBool(s string, clr color.Color) (bool, error) {
|
||||
s = strings.ToLower(s)
|
||||
if s == "true" || s == "yes" || s == "y" || s == "" {
|
||||
return true, nil
|
||||
}
|
||||
if s == "false" || s == "none" || s == "no" || s == "not" || s == "n" {
|
||||
return false, nil
|
||||
}
|
||||
i, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("`%s' couldn't converted to a %s", s, clr.Bold("bool"))
|
||||
}
|
||||
return i != 0, nil
|
||||
}
|
||||
|
||||
func getInt(s string, clr color.Color) (int64, error) {
|
||||
i, err := strconv.ParseInt(s, 0, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("`%s' couldn't converted to an %s", s, clr.Bold("int"))
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func getUint(s string, clr color.Color) (uint64, error) {
|
||||
i, err := strconv.ParseUint(s, 0, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("`%s' couldn't converted to an %s", s, clr.Bold("uint"))
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func getFloat(s string, clr color.Color) (float64, error) {
|
||||
f, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("`%s' couldn't converted to a %s", s, clr.Bold("float"))
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
Reference in new issue
Block a user