mirror of
https://github.com/danog/liquid.git
synced 2024-11-30 07:08:58 +01:00
Merge pull request #30 from nsf/master
Properly handle implicit conversion to integer types.
This commit is contained in:
commit
ca945a04d1
@ -31,6 +31,36 @@ func conversionError(modifier string, value interface{}, typ reflect.Type) error
|
||||
return typeErrorf("can't convert %s%T(%v) to type %s", modifier, value, value, typ)
|
||||
}
|
||||
|
||||
func convertValueToInt(value interface{}, typ reflect.Type) (int64, error) {
|
||||
switch value := value.(type) {
|
||||
case bool:
|
||||
if value {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
case string:
|
||||
v, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return 0, conversionError("", value, typ)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
return 0, conversionError("", value, typ)
|
||||
}
|
||||
|
||||
func convertValueToFloat(value interface{}, typ reflect.Type) (float64, error) {
|
||||
switch value := value.(type) {
|
||||
// case int is handled by rv.Convert(typ) in Convert function
|
||||
case string:
|
||||
v, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return 0, conversionError("", value, typ)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
return 0, conversionError("", value, typ)
|
||||
}
|
||||
|
||||
// Convert value to the type. This is a more aggressive conversion, that will
|
||||
// recursively create new map and slice values as necessary. It doesn't
|
||||
// handle circular references.
|
||||
@ -51,22 +81,42 @@ func Convert(value interface{}, typ reflect.Type) (interface{}, error) { // noli
|
||||
switch typ.Kind() {
|
||||
case reflect.Bool:
|
||||
return !(value == nil || value == false), nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
switch value := value.(type) {
|
||||
case bool:
|
||||
if value {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
case string:
|
||||
return strconv.Atoi(value)
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
switch value := value.(type) {
|
||||
// case int is handled by r.Convert(type) above
|
||||
case string:
|
||||
return strconv.ParseFloat(value, 64)
|
||||
}
|
||||
case reflect.Uint:
|
||||
v, err := convertValueToInt(value, typ)
|
||||
return uint(v), err
|
||||
case reflect.Uint8:
|
||||
v, err := convertValueToInt(value, typ)
|
||||
return uint8(v), err
|
||||
case reflect.Uint16:
|
||||
v, err := convertValueToInt(value, typ)
|
||||
return uint16(v), err
|
||||
case reflect.Uint32:
|
||||
v, err := convertValueToInt(value, typ)
|
||||
return uint32(v), err
|
||||
case reflect.Uint64:
|
||||
v, err := convertValueToInt(value, typ)
|
||||
return uint64(v), err
|
||||
case reflect.Int:
|
||||
v, err := convertValueToInt(value, typ)
|
||||
return int(v), err
|
||||
case reflect.Int8:
|
||||
v, err := convertValueToInt(value, typ)
|
||||
return int8(v), err
|
||||
case reflect.Int16:
|
||||
v, err := convertValueToInt(value, typ)
|
||||
return int16(v), err
|
||||
case reflect.Int32:
|
||||
v, err := convertValueToInt(value, typ)
|
||||
return int32(v), err
|
||||
case reflect.Int64:
|
||||
v, err := convertValueToInt(value, typ)
|
||||
return v, err
|
||||
case reflect.Float32:
|
||||
v, err := convertValueToFloat(value, typ)
|
||||
return float32(v), err
|
||||
case reflect.Float64:
|
||||
v, err := convertValueToFloat(value, typ)
|
||||
return v, err
|
||||
case reflect.Map:
|
||||
et := typ.Elem()
|
||||
result := reflect.MakeMap(typ)
|
||||
|
@ -23,7 +23,27 @@ var convertTests = []struct {
|
||||
}{
|
||||
{nil, false},
|
||||
{false, 0},
|
||||
{false, int(0)},
|
||||
{false, int8(0)},
|
||||
{false, int16(0)},
|
||||
{false, int32(0)},
|
||||
{false, int64(0)},
|
||||
{false, uint(0)},
|
||||
{false, uint8(0)},
|
||||
{false, uint16(0)},
|
||||
{false, uint32(0)},
|
||||
{false, uint64(0)},
|
||||
{true, 1},
|
||||
{true, int(1)},
|
||||
{true, int8(1)},
|
||||
{true, int16(1)},
|
||||
{true, int32(1)},
|
||||
{true, int64(1)},
|
||||
{true, uint(1)},
|
||||
{true, uint8(1)},
|
||||
{true, uint16(1)},
|
||||
{true, uint32(1)},
|
||||
{true, uint64(1)},
|
||||
{false, false},
|
||||
{true, true},
|
||||
{true, "true"},
|
||||
@ -33,10 +53,22 @@ var convertTests = []struct {
|
||||
{2, "2"},
|
||||
{2, 2.0},
|
||||
{"", true},
|
||||
{"2", int(2)},
|
||||
{"2", int8(2)},
|
||||
{"2", int16(2)},
|
||||
{"2", int32(2)},
|
||||
{"2", int64(2)},
|
||||
{"2", uint(2)},
|
||||
{"2", uint8(2)},
|
||||
{"2", uint16(2)},
|
||||
{"2", uint32(2)},
|
||||
{"2", uint64(2)},
|
||||
{"2", 2},
|
||||
{"2", 2.0},
|
||||
{"2.0", 2.0},
|
||||
{"2.1", 2.1},
|
||||
{"2.1", float32(2.1)},
|
||||
{"2.1", float64(2.1)},
|
||||
{"string", "string"},
|
||||
{[]interface{}{1, 2}, []interface{}{1, 2}},
|
||||
{[]int{1, 2}, []int{1, 2}},
|
||||
@ -57,11 +89,15 @@ var convertTests = []struct {
|
||||
}
|
||||
|
||||
var convertErrorTests = []struct {
|
||||
value, proto, expected interface{}
|
||||
value, proto interface{}
|
||||
expected []string
|
||||
}{
|
||||
{map[string]bool{"k": true}, map[int]bool{}, "map key"},
|
||||
{map[string]string{"k": "v"}, map[string]int{}, "map element"},
|
||||
{map[interface{}]interface{}{"k": "v"}, map[string]int{}, "map element"},
|
||||
{map[string]bool{"k": true}, map[int]bool{}, []string{"map key"}},
|
||||
{map[string]string{"k": "v"}, map[string]int{}, []string{"map element"}},
|
||||
{map[interface{}]interface{}{"k": "v"}, map[string]int{}, []string{"map element"}},
|
||||
{"notanumber", int(0), []string{"can't convert string", "to type int"}},
|
||||
{"notanumber", uint(0), []string{"can't convert string", "to type uint"}},
|
||||
{"notanumber", float64(0), []string{"can't convert string", "to type float64"}},
|
||||
}
|
||||
|
||||
func TestConvert(t *testing.T) {
|
||||
@ -83,7 +119,9 @@ func TestConvert_errors(t *testing.T) {
|
||||
name := fmt.Sprintf("Convert %#v -> %v", test.value, typ)
|
||||
_, err := Convert(test.value, typ)
|
||||
require.Errorf(t, err, name)
|
||||
require.Containsf(t, err.Error(), test.expected, name)
|
||||
for _, expected := range test.expected {
|
||||
require.Containsf(t, err.Error(), expected, name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user