github.com/tencent/goom@v1.0.1/arg/equals.go (about)

     1  package arg
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  	"strconv"
     8  	"strings"
     9  )
    10  
    11  // tryToBool attempts to convert the value 'v' to a boolean, returning
    12  // an error if it cannot. When converting a string, the function matches
    13  // true if the string nonempty and does not satisfy the condition for false
    14  // with parseBool https://golang.org/pkg/strconv/#ParseBool
    15  // and is not 0.0
    16  func tryToBool(v reflect.Value) (bool, error) {
    17  	if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
    18  		v = v.Elem()
    19  	}
    20  
    21  	switch v.Kind() {
    22  	case reflect.Float64, reflect.Float32:
    23  		return v.Float() != 0, nil
    24  	case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
    25  		return v.Int() != 0, nil
    26  	case reflect.Bool:
    27  		return v.Bool(), nil
    28  	case reflect.String:
    29  		if v.Len() == 0 {
    30  			return false, nil
    31  		}
    32  
    33  		s := v.String()
    34  		if b, err := strconv.ParseBool(s); err == nil && !b {
    35  			return false, nil
    36  		}
    37  
    38  		if f, err := tryToFloat64(v); err == nil && f == 0 {
    39  			return false, nil
    40  		}
    41  
    42  		return true, nil
    43  	case reflect.Slice, reflect.Map:
    44  		if v.Len() > 0 {
    45  			return true, nil
    46  		}
    47  
    48  		return false, nil
    49  	}
    50  	return false, errors.New("unknown type")
    51  }
    52  
    53  // tryToFloat64 attempts to convert a value to a float64.
    54  // If it cannot (in the case of a non-numeric string, a struct, etc.)
    55  // it matches 0.0 and an error.
    56  func tryToFloat64(v reflect.Value) (float64, error) {
    57  	if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
    58  		v = v.Elem()
    59  	}
    60  
    61  	switch v.Kind() {
    62  	case reflect.Float64, reflect.Float32:
    63  		return v.Float(), nil
    64  	case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
    65  		return float64(v.Int()), nil
    66  	case reflect.Bool:
    67  		if v.Bool() {
    68  			return 1, nil
    69  		}
    70  
    71  		return 0, nil
    72  	case reflect.String:
    73  		f, err := strconv.ParseFloat(v.String(), 64)
    74  		if err == nil {
    75  			return f, nil
    76  		}
    77  	}
    78  
    79  	return 0.0, errors.New("couldn't convert to a float64")
    80  }
    81  
    82  // tryToInt64 attempts to convert a value to an int64.
    83  // If it cannot (in the case of a non-numeric string, a struct, etc.)
    84  // it matches 0 and an error.
    85  func tryToInt64(v reflect.Value) (int64, error) {
    86  	if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
    87  		v = v.Elem()
    88  	}
    89  
    90  	switch v.Kind() {
    91  	case reflect.Float64, reflect.Float32:
    92  		return int64(v.Float()), nil
    93  	case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
    94  		return v.Int(), nil
    95  	case reflect.Bool:
    96  		if v.Bool() {
    97  			return 1, nil
    98  		}
    99  
   100  		return 0, nil
   101  	case reflect.String:
   102  		s := v.String()
   103  
   104  		var (
   105  			i   int64
   106  			err error
   107  		)
   108  
   109  		if strings.HasPrefix(s, "0x") {
   110  			i, err = strconv.ParseInt(s, 16, 64)
   111  		} else {
   112  			i, err = strconv.ParseInt(s, 10, 64)
   113  		}
   114  
   115  		if err == nil {
   116  			return i, nil
   117  		}
   118  	}
   119  	return 0, errors.New("couldn't convert to integer")
   120  }
   121  
   122  // isNil 判断是否为空
   123  func isNil(v reflect.Value) bool {
   124  	switch v.Kind() {
   125  	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
   126  		// from reflect IsNil:
   127  		// Note that IsNil is not always equivalent to a regular comparison with nil in Go.
   128  		// For example, if v was created by calling ValueOf with an uninitialized interface variable i,
   129  		// i==nil will be true but v.IsNil will panic as v will be the zero Value.
   130  		return v.IsNil()
   131  	default:
   132  		return false
   133  	}
   134  }
   135  
   136  // isNum 判断是否为数字
   137  func isNum(v reflect.Value) bool {
   138  	switch v.Kind() {
   139  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   140  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
   141  		reflect.Float32, reflect.Float64:
   142  		return true
   143  	}
   144  	return false
   145  }
   146  
   147  // tryToNumber tryToInt64 or tryToFloat64
   148  // errors if fail
   149  func tryToNumber(v reflect.Value) (reflect.Value, error) {
   150  	// If the LHS is a string formatted as an int, try that before trying float
   151  	lhsI, err := tryToInt64(v)
   152  	if err != nil {
   153  		// if LHS is a float, e.g. "1.2", we need to set v to a float64
   154  		lhsF, err := tryToFloat64(v)
   155  		if err != nil {
   156  			return reflect.Value{}, err
   157  		}
   158  		v = reflect.ValueOf(lhsF)
   159  	} else {
   160  		v = reflect.ValueOf(lhsI)
   161  	}
   162  	return v, nil
   163  }
   164  
   165  // equal matches true when lhsV and rhsV is same value.
   166  // nolint
   167  func equal(lhsV, rhsV reflect.Value) bool {
   168  	lhsIsNil, rhsIsNil := isNil(lhsV), isNil(rhsV)
   169  	if lhsIsNil && rhsIsNil {
   170  		return true
   171  	}
   172  
   173  	if (!lhsIsNil && rhsIsNil) || (lhsIsNil && !rhsIsNil) {
   174  		return false
   175  	}
   176  
   177  	if lhsV.Kind() == reflect.Interface || lhsV.Kind() == reflect.Ptr {
   178  		lhsV = lhsV.Elem()
   179  	}
   180  
   181  	if rhsV.Kind() == reflect.Interface || rhsV.Kind() == reflect.Ptr {
   182  		rhsV = rhsV.Elem()
   183  	}
   184  
   185  	if r, done := numStringEqual(lhsV, rhsV); done {
   186  		return r
   187  	}
   188  
   189  	if isNum(lhsV) && isNum(rhsV) {
   190  		return fmt.Sprintf("%v", lhsV) == fmt.Sprintf("%v", rhsV)
   191  	}
   192  
   193  	if r, done := boolEquals(lhsV, rhsV); done {
   194  		return r
   195  	}
   196  
   197  	return reflect.DeepEqual(lhsV.Interface(), rhsV.Interface())
   198  }
   199  
   200  func boolEquals(lhsV reflect.Value, rhsV reflect.Value) (bool, bool) {
   201  	// Try to compare bool values to the strings and numbers
   202  	if lhsV.Kind() == reflect.Bool || rhsV.Kind() == reflect.Bool {
   203  		lhsB, err := tryToBool(lhsV)
   204  		if err != nil {
   205  			return false, true
   206  		}
   207  
   208  		rhsB, err := tryToBool(rhsV)
   209  
   210  		if err != nil {
   211  			return false, true
   212  		}
   213  
   214  		return lhsB == rhsB, true
   215  	}
   216  	return false, false
   217  }
   218  
   219  func numStringEqual(lhsV reflect.Value, rhsV reflect.Value) (bool, bool) {
   220  	// Compare a string and a number.
   221  	// This will attempt to convert the string to a number,
   222  	// while leaving the other side alone. Code further
   223  	// down takes care of converting int values and floats as needed.
   224  	if isNum(lhsV) && rhsV.Kind() == reflect.String {
   225  		rhsF, err := tryToFloat64(rhsV)
   226  		if err != nil {
   227  			// Couldn't convert RHS to a float, they can't be compared.
   228  			return false, true
   229  		}
   230  
   231  		rhsV = reflect.ValueOf(rhsF)
   232  	} else if lhsV.Kind() == reflect.String && isNum(rhsV) {
   233  		num, err := tryToNumber(lhsV)
   234  		if err != nil {
   235  			return false, true
   236  		}
   237  
   238  		lhsV = num
   239  	} else {
   240  		return false, false
   241  	}
   242  	return reflect.DeepEqual(lhsV.Interface(), rhsV.Interface()), true
   243  }