github.com/hairyhenderson/gomplate/v3@v3.11.7/conv/conv.go (about) 1 // Package conv contains functions that help converting data between different types 2 package conv 3 4 import ( 5 "fmt" 6 "math" 7 "reflect" 8 "strconv" 9 "strings" 10 11 iconv "github.com/hairyhenderson/gomplate/v3/internal/conv" 12 "github.com/pkg/errors" 13 ) 14 15 // Bool converts a string to a boolean value, using strconv.ParseBool under the covers. 16 // Possible true values are: 1, t, T, TRUE, true, True 17 // All other values are considered false. 18 // 19 // See ToBool also for a more flexible version. 20 // Deprecated: use ToBool instead 21 func Bool(in string) bool { 22 if b, err := strconv.ParseBool(in); err == nil { 23 return b 24 } 25 return false 26 } 27 28 // ToBool converts an arbitrary input into a boolean. 29 // Possible non-boolean true values are: 1 or the strings "t", "true", or "yes" 30 // (any capitalizations) 31 // All other values are considered false. 32 func ToBool(in interface{}) bool { 33 if b, ok := in.(bool); ok { 34 return b 35 } 36 37 if str, ok := in.(string); ok { 38 str = strings.ToLower(str) 39 switch str { 40 case "1", "t", "true", "yes": 41 return true 42 default: 43 return strToFloat64(str) == 1 44 } 45 } 46 47 val := reflect.Indirect(reflect.ValueOf(in)) 48 switch val.Kind() { 49 case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: 50 return val.Int() == 1 51 case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: 52 return val.Uint() == 1 53 case reflect.Float32, reflect.Float64: 54 return val.Float() == 1 55 default: 56 return false 57 } 58 } 59 60 // ToBools - 61 func ToBools(in ...interface{}) []bool { 62 out := make([]bool, len(in)) 63 for i, v := range in { 64 out[i] = ToBool(v) 65 } 66 return out 67 } 68 69 // Slice creates a slice from a bunch of arguments 70 func Slice(args ...interface{}) []interface{} { 71 return args 72 } 73 74 // Join concatenates the elements of a to create a single string. 75 // The separator string sep is placed between elements in the resulting string. 76 // 77 // This is functionally identical to strings.Join, except that each element is 78 // coerced to a string first 79 func Join(in interface{}, sep string) (out string, err error) { 80 s, ok := in.([]string) 81 if ok { 82 return strings.Join(s, sep), nil 83 } 84 85 var a []interface{} 86 a, ok = in.([]interface{}) 87 if !ok { 88 a, err = iconv.InterfaceSlice(in) 89 if err != nil { 90 return "", errors.Wrap(err, "input to Join must be an array") 91 } 92 ok = true 93 } 94 if ok { 95 b := make([]string, len(a)) 96 for i := range a { 97 b[i] = ToString(a[i]) 98 } 99 return strings.Join(b, sep), nil 100 } 101 102 return "", errors.New("input to Join must be an array") 103 } 104 105 // Has determines whether or not a given object has a property with the given key 106 func Has(in interface{}, key interface{}) bool { 107 av := reflect.ValueOf(in) 108 109 switch av.Kind() { 110 case reflect.Map: 111 kv := reflect.ValueOf(key) 112 return av.MapIndex(kv).IsValid() 113 case reflect.Slice, reflect.Array: 114 l := av.Len() 115 for i := 0; i < l; i++ { 116 v := av.Index(i).Interface() 117 if reflect.DeepEqual(v, key) { 118 return true 119 } 120 } 121 } 122 123 return false 124 } 125 126 // ToString - 127 func ToString(in interface{}) string { 128 if in == nil { 129 return "nil" 130 } 131 if s, ok := in.(string); ok { 132 return s 133 } 134 if s, ok := in.(fmt.Stringer); ok { 135 return s.String() 136 } 137 if s, ok := in.([]byte); ok { 138 return string(s) 139 } 140 141 v, ok := printableValue(reflect.ValueOf(in)) 142 if ok { 143 in = v 144 } 145 return fmt.Sprint(in) 146 } 147 148 // ToStrings - 149 func ToStrings(in ...interface{}) []string { 150 out := make([]string, len(in)) 151 for i, v := range in { 152 out[i] = ToString(v) 153 } 154 return out 155 } 156 157 // MustParseInt - wrapper for strconv.ParseInt that returns 0 in the case of error 158 func MustParseInt(s string, base, bitSize int) int64 { 159 i, _ := strconv.ParseInt(s, base, bitSize) 160 return i 161 } 162 163 // MustParseFloat - wrapper for strconv.ParseFloat that returns 0 in the case of error 164 func MustParseFloat(s string, bitSize int) float64 { 165 i, _ := strconv.ParseFloat(s, bitSize) 166 return i 167 } 168 169 // MustParseUint - wrapper for strconv.ParseUint that returns 0 in the case of error 170 func MustParseUint(s string, base, bitSize int) uint64 { 171 i, _ := strconv.ParseUint(s, base, bitSize) 172 return i 173 } 174 175 // MustAtoi - wrapper for strconv.Atoi that returns 0 in the case of error 176 func MustAtoi(s string) int { 177 i, _ := strconv.Atoi(s) 178 return i 179 } 180 181 // ToInt64 - convert input to an int64, if convertible. Otherwise, returns 0. 182 func ToInt64(v interface{}) int64 { 183 if str, ok := v.(string); ok { 184 return strToInt64(str) 185 } 186 187 val := reflect.Indirect(reflect.ValueOf(v)) 188 switch val.Kind() { 189 case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: 190 return val.Int() 191 case reflect.Uint8, reflect.Uint16, reflect.Uint32: 192 return int64(val.Uint()) 193 case reflect.Uint, reflect.Uint64: 194 tv := val.Uint() 195 // this can overflow and give -1, but IMO this is better than 196 // returning maxint64 197 return int64(tv) 198 case reflect.Float32, reflect.Float64: 199 return int64(val.Float()) 200 case reflect.Bool: 201 if val.Bool() { 202 return 1 203 } 204 return 0 205 default: 206 return 0 207 } 208 } 209 210 // ToInt - 211 func ToInt(in interface{}) int { 212 // Protect against CWE-190 and CWE-681 213 // https://cwe.mitre.org/data/definitions/190.html 214 // https://cwe.mitre.org/data/definitions/681.html 215 if i := ToInt64(in); i <= math.MaxInt || i >= math.MinInt { 216 return int(i) 217 } 218 219 // we're probably on a 32-bit system, so we can't represent this number 220 return -1 221 } 222 223 // ToInt64s - 224 func ToInt64s(in ...interface{}) []int64 { 225 out := make([]int64, len(in)) 226 for i, v := range in { 227 out[i] = ToInt64(v) 228 } 229 return out 230 } 231 232 // ToInts - 233 func ToInts(in ...interface{}) []int { 234 out := make([]int, len(in)) 235 for i, v := range in { 236 out[i] = ToInt(v) 237 } 238 return out 239 } 240 241 // ToFloat64 - convert input to a float64, if convertible. Otherwise, returns 0. 242 func ToFloat64(v interface{}) float64 { 243 if str, ok := v.(string); ok { 244 return strToFloat64(str) 245 } 246 247 val := reflect.Indirect(reflect.ValueOf(v)) 248 switch val.Kind() { 249 case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: 250 return float64(val.Int()) 251 case reflect.Uint8, reflect.Uint16, reflect.Uint32: 252 return float64(val.Uint()) 253 case reflect.Uint, reflect.Uint64: 254 return float64(val.Uint()) 255 case reflect.Float32, reflect.Float64: 256 return val.Float() 257 case reflect.Bool: 258 if val.Bool() { 259 return 1 260 } 261 return 0 262 default: 263 return 0 264 } 265 } 266 267 func strToInt64(str string) int64 { 268 if strings.Contains(str, ",") { 269 str = strings.ReplaceAll(str, ",", "") 270 } 271 iv, err := strconv.ParseInt(str, 0, 64) 272 if err != nil { 273 // maybe it's a float? 274 var fv float64 275 fv, err = strconv.ParseFloat(str, 64) 276 if err != nil { 277 return 0 278 } 279 return ToInt64(fv) 280 } 281 return iv 282 } 283 284 func strToFloat64(str string) float64 { 285 if strings.Contains(str, ",") { 286 str = strings.ReplaceAll(str, ",", "") 287 } 288 // this is inefficient, but it's the only way I can think of to 289 // properly convert octal integers to floats 290 iv, err := strconv.ParseInt(str, 0, 64) 291 if err != nil { 292 // ok maybe it's a float? 293 var fv float64 294 fv, err = strconv.ParseFloat(str, 64) 295 if err != nil { 296 return 0 297 } 298 return fv 299 } 300 return float64(iv) 301 } 302 303 // ToFloat64s - 304 func ToFloat64s(in ...interface{}) []float64 { 305 out := make([]float64, len(in)) 306 for i, v := range in { 307 out[i] = ToFloat64(v) 308 } 309 return out 310 } 311 312 // Dict is a convenience function that creates a map with string keys. 313 // Provide arguments as key/value pairs. If an odd number of arguments 314 // is provided, the last is used as the key, and an empty string is 315 // set as the value. 316 // All keys are converted to strings, regardless of input type. 317 func Dict(v ...interface{}) (map[string]interface{}, error) { 318 dict := map[string]interface{}{} 319 lenv := len(v) 320 for i := 0; i < lenv; i += 2 { 321 key := ToString(v[i]) 322 if i+1 >= lenv { 323 dict[key] = "" 324 continue 325 } 326 dict[key] = v[i+1] 327 } 328 return dict, nil 329 }