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