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  }