Initial Commit
This commit is contained in:
		
							
								
								
									
										149
									
								
								internal/reflectutil/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								internal/reflectutil/utils.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,149 @@
 | 
			
		||||
package reflectutil
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
 | 
			
		||||
	"github.com/mitchellh/mapstructure"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Convert attempts to convert the given value to the given type
 | 
			
		||||
func Convert(in reflect.Value, toType reflect.Type) (reflect.Value, error) {
 | 
			
		||||
	// Get input type
 | 
			
		||||
	inType := in.Type()
 | 
			
		||||
 | 
			
		||||
	// If input is already the desired type, return
 | 
			
		||||
	if inType == toType {
 | 
			
		||||
		return in, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If input can be converted to desired type, convert and return
 | 
			
		||||
	if in.CanConvert(toType) {
 | 
			
		||||
		return in.Convert(toType), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create new value of desired type
 | 
			
		||||
	to := reflect.New(toType).Elem()
 | 
			
		||||
 | 
			
		||||
	// If type is a pointer
 | 
			
		||||
	if to.Kind() == reflect.Pointer {
 | 
			
		||||
		// Initialize value
 | 
			
		||||
		to.Set(reflect.New(to.Type().Elem()))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch val := in.Interface().(type) {
 | 
			
		||||
	case string:
 | 
			
		||||
		// If desired type satisfies text unmarshaler
 | 
			
		||||
		if u, ok := to.Interface().(encoding.TextUnmarshaler); ok {
 | 
			
		||||
			// Use text unmarshaler to get value
 | 
			
		||||
			err := u.UnmarshalText([]byte(val))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return reflect.Value{}, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Return unmarshaled value
 | 
			
		||||
			return reflect.ValueOf(any(u)), nil
 | 
			
		||||
		}
 | 
			
		||||
	case []byte:
 | 
			
		||||
		// If desired type satisfies binary unmarshaler
 | 
			
		||||
		if u, ok := to.Interface().(encoding.BinaryUnmarshaler); ok {
 | 
			
		||||
			// Use binary unmarshaler to get value
 | 
			
		||||
			err := u.UnmarshalBinary(val)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return reflect.Value{}, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Return unmarshaled value
 | 
			
		||||
			return reflect.ValueOf(any(u)), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If input is a map
 | 
			
		||||
	if in.Kind() == reflect.Map {
 | 
			
		||||
		// Use mapstructure to decode value
 | 
			
		||||
		err := mapstructure.Decode(in.Interface(), to.Addr().Interface())
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return to, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If input is a slice of any, and output is an array or slice
 | 
			
		||||
	if in.Type() == reflect.TypeOf([]any{}) &&
 | 
			
		||||
		to.Kind() == reflect.Slice || to.Kind() == reflect.Array {
 | 
			
		||||
			// Use ConvertSlice to convert value
 | 
			
		||||
			to.Set(reflect.ValueOf(ConvertSlice(
 | 
			
		||||
			in.Interface().([]any),
 | 
			
		||||
			toType,
 | 
			
		||||
		)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return to, fmt.Errorf("cannot convert %s to %s", inType, toType)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertSlice converts []any to an array or slice, as provided
 | 
			
		||||
// in the "to" argument.
 | 
			
		||||
func ConvertSlice(in []any, to reflect.Type) any {
 | 
			
		||||
	// Create new value for output
 | 
			
		||||
	out := reflect.New(to).Elem()
 | 
			
		||||
 | 
			
		||||
	// If output value is a slice
 | 
			
		||||
	if out.Kind() == reflect.Slice {
 | 
			
		||||
		// Get type of slice elements
 | 
			
		||||
		outType := out.Type().Elem()
 | 
			
		||||
 | 
			
		||||
		// For every value provided
 | 
			
		||||
		for i := 0; i < len(in); i++ {
 | 
			
		||||
			// Get value of input type
 | 
			
		||||
			inVal := reflect.ValueOf(in[i])
 | 
			
		||||
			// Create new output type
 | 
			
		||||
			outVal := reflect.New(outType).Elem()
 | 
			
		||||
 | 
			
		||||
			// If types match
 | 
			
		||||
			if inVal.Type() == outType {
 | 
			
		||||
				// Set output value to input value
 | 
			
		||||
				outVal.Set(inVal)
 | 
			
		||||
			} else {
 | 
			
		||||
				// If input value can be converted to output type
 | 
			
		||||
				if inVal.CanConvert(outType) {
 | 
			
		||||
					// Convert and set output value to input value
 | 
			
		||||
					outVal.Set(inVal.Convert(outType))
 | 
			
		||||
				} else {
 | 
			
		||||
					// Set output value to its zero value
 | 
			
		||||
					outVal.Set(reflect.Zero(outVal.Type()))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Append output value to slice
 | 
			
		||||
			out = reflect.Append(out, outVal)
 | 
			
		||||
		}
 | 
			
		||||
	} else if out.Kind() == reflect.Array && out.Len() == len(in) {
 | 
			
		||||
		//If output type is array and lengths match
 | 
			
		||||
 | 
			
		||||
		// For every input value
 | 
			
		||||
		for i := 0; i < len(in); i++ {
 | 
			
		||||
			// Get matching output index
 | 
			
		||||
			outVal := out.Index(i)
 | 
			
		||||
			// Get input value
 | 
			
		||||
			inVal := reflect.ValueOf(in[i])
 | 
			
		||||
 | 
			
		||||
			// If types match
 | 
			
		||||
			if inVal.Type() == outVal.Type() {
 | 
			
		||||
				// Set output value to input value
 | 
			
		||||
				outVal.Set(inVal)
 | 
			
		||||
			} else {
 | 
			
		||||
				// If input value can be converted to output type
 | 
			
		||||
				if inVal.CanConvert(outVal.Type()) {
 | 
			
		||||
					// Convert and set output value to input value
 | 
			
		||||
					outVal.Set(inVal.Convert(outVal.Type()))
 | 
			
		||||
				} else {
 | 
			
		||||
					// Set output value to its zero value
 | 
			
		||||
					outVal.Set(reflect.Zero(outVal.Type()))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Return created value
 | 
			
		||||
	return out.Interface()
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user