package sprig import ( "fmt" "math" "reflect" "strconv" "strings" ) // toFloat64 converts 64-bit floats func toFloat64(v interface{}) float64 { if str, ok := v.(string); ok { iv, err := strconv.ParseFloat(str, 64) if err != nil { return 0 } return iv } val := reflect.Indirect(reflect.ValueOf(v)) switch val.Kind() { case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: return float64(val.Int()) case reflect.Uint8, reflect.Uint16, reflect.Uint32: return float64(val.Uint()) case reflect.Uint, reflect.Uint64: return float64(val.Uint()) case reflect.Float32, reflect.Float64: return val.Float() case reflect.Bool: if val.Bool() { return 1 } return 0 default: return 0 } } func toInt(v interface{}) int { //It's not optimal. Bud I don't want duplicate toInt64 code. return int(toInt64(v)) } // toInt64 converts integer types to 64-bit integers func toInt64(v interface{}) int64 { if str, ok := v.(string); ok { iv, err := strconv.ParseInt(str, 10, 64) if err != nil { return 0 } return iv } val := reflect.Indirect(reflect.ValueOf(v)) switch val.Kind() { case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: return val.Int() case reflect.Uint8, reflect.Uint16, reflect.Uint32: return int64(val.Uint()) case reflect.Uint, reflect.Uint64: tv := val.Uint() if tv <= math.MaxInt64 { return int64(tv) } // TODO: What is the sensible thing to do here? return math.MaxInt64 case reflect.Float32, reflect.Float64: return int64(val.Float()) case reflect.Bool: if val.Bool() { return 1 } return 0 default: return 0 } } func max(a interface{}, i ...interface{}) int64 { aa := toInt64(a) for _, b := range i { bb := toInt64(b) if bb > aa { aa = bb } } return aa } func maxf(a interface{}, i ...interface{}) float64 { aa := toFloat64(a) for _, b := range i { bb := toFloat64(b) aa = math.Max(aa, bb) } return aa } func min(a interface{}, i ...interface{}) int64 { aa := toInt64(a) for _, b := range i { bb := toInt64(b) if bb < aa { aa = bb } } return aa } func minf(a interface{}, i ...interface{}) float64 { aa := toFloat64(a) for _, b := range i { bb := toFloat64(b) aa = math.Min(aa, bb) } return aa } func until(count int) []int { step := 1 if count < 0 { step = -1 } return untilStep(0, count, step) } func untilStep(start, stop, step int) []int { v := []int{} if stop < start { if step >= 0 { return v } for i := start; i > stop; i += step { v = append(v, i) } return v } if step <= 0 { return v } for i := start; i < stop; i += step { v = append(v, i) } return v } func floor(a interface{}) float64 { aa := toFloat64(a) return math.Floor(aa) } func ceil(a interface{}) float64 { aa := toFloat64(a) return math.Ceil(aa) } func round(a interface{}, p int, rOpt ...float64) float64 { roundOn := .5 if len(rOpt) > 0 { roundOn = rOpt[0] } val := toFloat64(a) places := toFloat64(p) var round float64 pow := math.Pow(10, places) digit := pow * val _, div := math.Modf(digit) if div >= roundOn { round = math.Ceil(digit) } else { round = math.Floor(digit) } return round / pow } // converts unix octal to decimal func toDecimal(v interface{}) int64 { result, err := strconv.ParseInt(fmt.Sprint(v), 8, 64) if err != nil { return 0 } return result } func seq(params ...int) string { increment := 1 switch len(params) { case 0: return "" case 1: start := 1 end := params[0] if end < start { increment = -1 } return intArrayToString(untilStep(start, end+increment, increment), " ") case 3: start := params[0] end := params[2] step := params[1] if end < start { increment = -1 if step > 0 { return "" } } return intArrayToString(untilStep(start, end+increment, step), " ") case 2: start := params[0] end := params[1] step := 1 if end < start { step = -1 } return intArrayToString(untilStep(start, end+step, step), " ") default: return "" } } func intArrayToString(slice []int, delimeter string) string { return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(slice)), delimeter), "[]") }