Upstream urfave/cli state of the touched files before 3510d4c9d9

This commit is contained in:
upstream committed 2026-09-04 09:31:42 +02:00
commit 8a9f09fc09
2 files changed
+1246

No files matched your search

+517
View File
@@ -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
}