|
- package tasks
-
- import (
- "context"
- "encoding/base64"
- "encoding/json"
- "fmt"
- "reflect"
- "strings"
- )
-
- var (
- typesMap = map[string]reflect.Type{
- // base types
- "bool": reflect.TypeOf(true),
- "int": reflect.TypeOf(int(1)),
- "int8": reflect.TypeOf(int8(1)),
- "int16": reflect.TypeOf(int16(1)),
- "int32": reflect.TypeOf(int32(1)),
- "int64": reflect.TypeOf(int64(1)),
- "uint": reflect.TypeOf(uint(1)),
- "uint8": reflect.TypeOf(uint8(1)),
- "uint16": reflect.TypeOf(uint16(1)),
- "uint32": reflect.TypeOf(uint32(1)),
- "uint64": reflect.TypeOf(uint64(1)),
- "float32": reflect.TypeOf(float32(0.5)),
- "float64": reflect.TypeOf(float64(0.5)),
- "string": reflect.TypeOf(string("")),
- // slices
- "[]bool": reflect.TypeOf(make([]bool, 0)),
- "[]int": reflect.TypeOf(make([]int, 0)),
- "[]int8": reflect.TypeOf(make([]int8, 0)),
- "[]int16": reflect.TypeOf(make([]int16, 0)),
- "[]int32": reflect.TypeOf(make([]int32, 0)),
- "[]int64": reflect.TypeOf(make([]int64, 0)),
- "[]uint": reflect.TypeOf(make([]uint, 0)),
- "[]uint8": reflect.TypeOf(make([]uint8, 0)),
- "[]uint16": reflect.TypeOf(make([]uint16, 0)),
- "[]uint32": reflect.TypeOf(make([]uint32, 0)),
- "[]uint64": reflect.TypeOf(make([]uint64, 0)),
- "[]float32": reflect.TypeOf(make([]float32, 0)),
- "[]float64": reflect.TypeOf(make([]float64, 0)),
- "[]byte": reflect.TypeOf(make([]byte, 0)),
- "[]string": reflect.TypeOf([]string{""}),
- }
-
- ctxType = reflect.TypeOf((*context.Context)(nil)).Elem()
-
- typeConversionError = func(argValue interface{}, argTypeStr string) error {
- return fmt.Errorf("%v is not %v", argValue, argTypeStr)
- }
- )
-
- // ErrUnsupportedType ...
- type ErrUnsupportedType struct {
- valueType string
- }
-
- // NewErrUnsupportedType returns new ErrUnsupportedType
- func NewErrUnsupportedType(valueType string) ErrUnsupportedType {
- return ErrUnsupportedType{valueType}
- }
-
- // Error method so we implement the error interface
- func (e ErrUnsupportedType) Error() string {
- return fmt.Sprintf("%v is not one of supported types", e.valueType)
- }
-
- // ReflectValue converts interface{} to reflect.Value based on string type
- func ReflectValue(valueType string, value interface{}) (reflect.Value, error) {
- if strings.HasPrefix(valueType, "[]") {
- return reflectValues(valueType, value)
- }
-
- return reflectValue(valueType, value)
- }
-
- // reflectValue converts interface{} to reflect.Value based on string type
- // representing a base type (not a slice)
- func reflectValue(valueType string, value interface{}) (reflect.Value, error) {
- theType, ok := typesMap[valueType]
- if !ok {
- return reflect.Value{}, NewErrUnsupportedType(valueType)
- }
- theValue := reflect.New(theType)
-
- // Booleans
- if theType.String() == "bool" {
- boolValue, err := getBoolValue(theType.String(), value)
- if err != nil {
- return reflect.Value{}, err
- }
-
- theValue.Elem().SetBool(boolValue)
- return theValue.Elem(), nil
- }
-
- // Integers
- if strings.HasPrefix(theType.String(), "int") {
- intValue, err := getIntValue(theType.String(), value)
- if err != nil {
- return reflect.Value{}, err
- }
-
- theValue.Elem().SetInt(intValue)
- return theValue.Elem(), err
- }
-
- // Unsigned integers
- if strings.HasPrefix(theType.String(), "uint") {
- uintValue, err := getUintValue(theType.String(), value)
- if err != nil {
- return reflect.Value{}, err
- }
-
- theValue.Elem().SetUint(uintValue)
- return theValue.Elem(), err
- }
-
- // Floating point numbers
- if strings.HasPrefix(theType.String(), "float") {
- floatValue, err := getFloatValue(theType.String(), value)
- if err != nil {
- return reflect.Value{}, err
- }
-
- theValue.Elem().SetFloat(floatValue)
- return theValue.Elem(), err
- }
-
- // Strings
- if theType.String() == "string" {
- stringValue, err := getStringValue(theType.String(), value)
- if err != nil {
- return reflect.Value{}, err
- }
-
- theValue.Elem().SetString(stringValue)
- return theValue.Elem(), nil
- }
-
- return reflect.Value{}, NewErrUnsupportedType(valueType)
- }
-
- // reflectValues converts interface{} to reflect.Value based on string type
- // representing a slice of values
- func reflectValues(valueType string, value interface{}) (reflect.Value, error) {
- theType, ok := typesMap[valueType]
- if !ok {
- return reflect.Value{}, NewErrUnsupportedType(valueType)
- }
-
- // For NULL we return an empty slice
- if value == nil {
- return reflect.MakeSlice(theType, 0, 0), nil
- }
-
- var theValue reflect.Value
-
- // Booleans
- if theType.String() == "[]bool" {
- bools := reflect.ValueOf(value)
-
- theValue = reflect.MakeSlice(theType, bools.Len(), bools.Len())
- for i := 0; i < bools.Len(); i++ {
- boolValue, err := getBoolValue(strings.Split(theType.String(), "[]")[1], bools.Index(i).Interface())
- if err != nil {
- return reflect.Value{}, err
- }
-
- theValue.Index(i).SetBool(boolValue)
- }
-
- return theValue, nil
- }
-
- // Integers
- if strings.HasPrefix(theType.String(), "[]int") {
- ints := reflect.ValueOf(value)
-
- theValue = reflect.MakeSlice(theType, ints.Len(), ints.Len())
- for i := 0; i < ints.Len(); i++ {
- intValue, err := getIntValue(strings.Split(theType.String(), "[]")[1], ints.Index(i).Interface())
- if err != nil {
- return reflect.Value{}, err
- }
-
- theValue.Index(i).SetInt(intValue)
- }
-
- return theValue, nil
- }
-
- // Unsigned integers
- if strings.HasPrefix(theType.String(), "[]uint") || theType.String() == "[]byte" {
-
- // Decode the base64 string if the value type is []uint8 or it's alias []byte
- // See: https://golang.org/pkg/encoding/json/#Marshal
- // > Array and slice values encode as JSON arrays, except that []byte encodes as a base64-encoded string
- if reflect.TypeOf(value).String() == "string" {
- output, err := base64.StdEncoding.DecodeString(value.(string))
- if err != nil {
- return reflect.Value{}, err
- }
- value = output
- }
-
- uints := reflect.ValueOf(value)
-
- theValue = reflect.MakeSlice(theType, uints.Len(), uints.Len())
- for i := 0; i < uints.Len(); i++ {
- uintValue, err := getUintValue(strings.Split(theType.String(), "[]")[1], uints.Index(i).Interface())
- if err != nil {
- return reflect.Value{}, err
- }
-
- theValue.Index(i).SetUint(uintValue)
- }
-
- return theValue, nil
- }
-
- // Floating point numbers
- if strings.HasPrefix(theType.String(), "[]float") {
- floats := reflect.ValueOf(value)
-
- theValue = reflect.MakeSlice(theType, floats.Len(), floats.Len())
- for i := 0; i < floats.Len(); i++ {
- floatValue, err := getFloatValue(strings.Split(theType.String(), "[]")[1], floats.Index(i).Interface())
- if err != nil {
- return reflect.Value{}, err
- }
-
- theValue.Index(i).SetFloat(floatValue)
- }
-
- return theValue, nil
- }
-
- // Strings
- if theType.String() == "[]string" {
- strs := reflect.ValueOf(value)
-
- theValue = reflect.MakeSlice(theType, strs.Len(), strs.Len())
- for i := 0; i < strs.Len(); i++ {
- strValue, err := getStringValue(strings.Split(theType.String(), "[]")[1], strs.Index(i).Interface())
- if err != nil {
- return reflect.Value{}, err
- }
-
- theValue.Index(i).SetString(strValue)
- }
-
- return theValue, nil
- }
-
- return reflect.Value{}, NewErrUnsupportedType(valueType)
- }
-
- func getBoolValue(theType string, value interface{}) (bool, error) {
- b, ok := value.(bool)
- if !ok {
- return false, typeConversionError(value, typesMap[theType].String())
- }
-
- return b, nil
- }
-
- func getIntValue(theType string, value interface{}) (int64, error) {
- // We use https://golang.org/pkg/encoding/json/#Decoder.UseNumber when unmarshaling signatures.
- // This is because JSON only supports 64-bit floating point numbers and we could lose precision
- // when converting from float64 to signed integer
- if strings.HasPrefix(fmt.Sprintf("%T", value), "json.Number") {
- n, ok := value.(json.Number)
- if !ok {
- return 0, typeConversionError(value, typesMap[theType].String())
- }
-
- return n.Int64()
- }
-
- n, ok := value.(int64)
- if !ok {
- return 0, typeConversionError(value, typesMap[theType].String())
- }
-
- return n, nil
- }
-
- func getUintValue(theType string, value interface{}) (uint64, error) {
- // We use https://golang.org/pkg/encoding/json/#Decoder.UseNumber when unmarshaling signatures.
- // This is because JSON only supports 64-bit floating point numbers and we could lose precision
- // when converting from float64 to unsigned integer
- if strings.HasPrefix(fmt.Sprintf("%T", value), "json.Number") {
- n, ok := value.(json.Number)
- if !ok {
- return 0, typeConversionError(value, typesMap[theType].String())
- }
-
- intVal, err := n.Int64()
- if err != nil {
- return 0, err
- }
-
- return uint64(intVal), nil
- }
-
- var n uint64
- switch value.(type) {
- case uint64:
- n = value.(uint64)
- case uint8:
- n = uint64(value.(uint8))
- default:
- return 0, typeConversionError(value, typesMap[theType].String())
- }
- return n, nil
- }
-
- func getFloatValue(theType string, value interface{}) (float64, error) {
- // We use https://golang.org/pkg/encoding/json/#Decoder.UseNumber when unmarshaling signatures.
- // This is because JSON only supports 64-bit floating point numbers and we could lose precision
- if strings.HasPrefix(fmt.Sprintf("%T", value), "json.Number") {
- n, ok := value.(json.Number)
- if !ok {
- return 0, typeConversionError(value, typesMap[theType].String())
- }
-
- return n.Float64()
- }
-
- f, ok := value.(float64)
- if !ok {
- return 0, typeConversionError(value, typesMap[theType].String())
- }
-
- return f, nil
- }
-
- func getStringValue(theType string, value interface{}) (string, error) {
- s, ok := value.(string)
- if !ok {
- return "", typeConversionError(value, typesMap[theType].String())
- }
-
- return s, nil
- }
-
- // IsContextType checks to see if the type is a context.Context
- func IsContextType(t reflect.Type) bool {
- return t == ctxType
- }
|