1
0
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:
Oliver Steele 2018-06-05 15:27:13 -04:00 committed by GitHub
commit ca945a04d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 109 additions and 21 deletions

View File

@ -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)

View File

@ -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)
}
})
}
}