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 }