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