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  }