package gou import ( "encoding/json" "fmt" "math" "strconv" "strings" ) // Coerce types (string,int,int64, float, []byte) into String type func CoerceString(v interface{}) (string, error) { switch val := v.(type) { case string: if val == "null" || val == "NULL" { return "", nil } return val, nil case int: return strconv.Itoa(val), nil case int32: return strconv.FormatInt(int64(val), 10), nil case int64: return strconv.FormatInt(val, 10), nil case uint32: return strconv.FormatUint(uint64(val), 10), nil case uint64: return strconv.FormatUint(val, 10), nil case float32: return strconv.FormatFloat(float64(val), 'f', -1, 32), nil case float64: return strconv.FormatFloat(val, 'f', -1, 64), nil case bool: if val { return "true", nil } return "false", nil case []byte: if string(val) == "null" || string(val) == "NULL" { return "", nil } return string(val), nil case json.RawMessage: if string(val) == "null" || string(val) == "NULL" { return "", nil } return string(val), nil } return "", fmt.Errorf("Could not coerce to string: %v", v) } // Coerce type to string, returning zero length string if error or nil func CoerceStringShort(v interface{}) string { val, _ := CoerceString(v) return val } // CoerceStrings Coerce type to strings, will split on comma by default. func CoerceStrings(v interface{}) []string { switch val := v.(type) { case string: if val == "" { return nil } return strings.Split(val, ",") case []string: return val case []interface{}: sva := make([]string, 0) for _, av := range val { switch aval := av.(type) { case string: sva = append(sva, aval) default: sv, err := CoerceString(av) if err == nil && sv != "" { sva = append(sva, sv) } } } return sva } return []string{CoerceStringShort(v)} } func CoerceFloats(v interface{}) []float64 { switch val := v.(type) { case float64: return []float64{val} case []string: fa := make([]float64, 0) for _, av := range val { f, err := CoerceFloat(av) if err == nil && !math.IsNaN(f) { fa = append(fa, f) } } return fa case []interface{}: fa := make([]float64, 0) for _, av := range val { switch aval := av.(type) { case float64: fa = append(fa, aval) default: f, err := CoerceFloat(av) if err == nil && !math.IsNaN(f) { fa = append(fa, f) } } } return fa } fv, err := CoerceFloat(v) if err == nil { return []float64{fv} } return nil } func CoerceFloat(v interface{}) (float64, error) { switch val := v.(type) { case int: return float64(val), nil case int32: return float64(val), nil case int64: return float64(val), nil case uint32: return float64(val), nil case uint64: return float64(val), nil case float64: return val, nil case string: if len(val) > 0 { if iv, err := strconv.ParseFloat(val, 64); err == nil { return iv, nil } } case []byte: if len(val) > 0 { if iv, err := strconv.ParseFloat(string(val), 64); err == nil { return iv, nil } } case json.RawMessage: if len(val) > 0 { if iv, err := strconv.ParseFloat(string(val), 64); err == nil { return iv, nil } } case nil: return math.NaN(), nil } return 0, fmt.Errorf("Could not Coerce Value: %v", v) } func CoerceFloatShort(v interface{}) float64 { val, _ := CoerceFloat(v) return val } func CoerceInt64(v interface{}) (int64, error) { val, ok := valToInt64(v) if ok { return val, nil } return 0, fmt.Errorf("Could not coerce to int64: %v", v) } func CoerceInt64Short(v interface{}) int64 { val, ok := valToInt64(v) if ok { return val } return 0 } func CoerceInt(v interface{}) (int, error) { val, ok := valToInt(v) if ok { return val, nil } return 0, fmt.Errorf("Could not coerce to int64: %v", v) } func CoerceIntShort(v interface{}) int { val, ok := valToInt(v) if ok { return val } return 0 } func CoerceInts(v interface{}) []int { switch val := v.(type) { case []string: iva := make([]int, 0) for _, av := range val { avAsInt, ok := valToInt(av) if ok { iva = append(iva, avAsInt) } } return iva case []interface{}: iva := make([]int, 0) for _, av := range val { avAsInt, ok := valToInt(av) if ok { iva = append(iva, avAsInt) } } return iva } return []int{CoerceIntShort(v)} } // Coerce a val(interface{}) into a Uint64 func CoerceUint(v interface{}) (uint64, error) { u64, ok := valToUint64(v) if !ok { return 0, fmt.Errorf("Could not Coerce %v", v) } return u64, nil } // Coerce a Val(interface{}) into Uint64 func CoerceUintShort(v interface{}) uint64 { val, _ := CoerceUint(v) return val } // Given any numeric type (float*, int*, uint*, string) return an int. Returns false if it would // overflow or if the the argument is not numeric. func valToInt(i interface{}) (int, bool) { i64, ok := valToInt64(i) if !ok { return -1, false } if i64 > MaxInt || i64 < MinInt { return -1, false } return int(i64), true } // Given any simple type (float*, int*, uint*, string, []byte, json.RawMessage) return an int64. // Returns false if it would overflow or if the the argument is not numeric. func valToInt64(i interface{}) (int64, bool) { switch x := i.(type) { case float32: return int64(x), true case float64: return int64(x), true case uint8: return int64(x), true case uint16: return int64(x), true case uint32: return int64(x), true case uint64: if x > math.MaxInt64 { return 0, false } return int64(x), true case int8: return int64(x), true case int16: return int64(x), true case int32: return int64(x), true case int64: return int64(x), true case int: return int64(x), true case uint: if uint64(x) > math.MaxInt64 { return 0, false } return int64(x), true case string: if len(x) > 0 { if iv, err := strconv.ParseInt(x, 10, 64); err == nil { return iv, true } if iv, err := strconv.ParseFloat(x, 64); err == nil { return valToInt64(iv) } } case []byte: if len(x) > 0 { if iv, err := strconv.ParseInt(string(x), 10, 64); err == nil { return iv, true } if iv, err := strconv.ParseFloat(string(x), 64); err == nil { return valToInt64(iv) } } case json.RawMessage: if len(x) > 0 { if iv, err := strconv.ParseInt(string(x), 10, 64); err == nil { return iv, true } if iv, err := strconv.ParseFloat(string(x), 64); err == nil { return valToInt64(iv) } } } return 0, false } // Given any simple type (float*, int*, uint*, string, []byte, json.RawMessage) return an int64. // Returns false if it would overflow or if the the argument is not numeric. func valToUint64(i interface{}) (uint64, bool) { switch x := i.(type) { case float32: return uint64(x), true case float64: return uint64(x), true case uint8: return uint64(x), true case uint16: return uint64(x), true case uint32: return uint64(x), true case uint64: return x, true case int8: return uint64(x), true case int16: return uint64(x), true case int32: return uint64(x), true case int64: return uint64(x), true case int: return uint64(x), true case uint: return uint64(x), true case string: if len(x) > 0 { if uiv, err := strconv.ParseUint(x, 10, 64); err == nil { return uiv, true } if fv, err := strconv.ParseFloat(x, 64); err == nil { return uint64(fv), true } } case []byte: if len(x) > 0 { if uiv, err := strconv.ParseUint(string(x), 10, 64); err == nil { return uiv, true } if fv, err := strconv.ParseFloat(string(x), 64); err == nil { return uint64(fv), true } } case json.RawMessage: if len(x) > 0 { if uiv, err := strconv.ParseUint(string(x), 10, 64); err == nil { return uiv, true } if fv, err := strconv.ParseFloat(string(x), 64); err == nil { return uint64(fv), true } } } return 0, false }