1
0
mirror of https://github.com/danog/liquid.git synced 2024-11-26 23:34:47 +01:00

Properly handle implicit conversion to integer types.

Go's reflect API is very picky about arguments to Call method. You can't just
pass in the "int" when function expects "int64", even on 64 bit machines. This
patch solves this problem by performing conversions properly. Truncation
problems however, as well as negative to uint issues are completely ignored.
Which is perhaps not ideal, but still better than returning "int" when "int32"
or "int64" is requested.

I did split the Kind switch cases and moved value -> int and value -> float
conversions to separate functions. An alternative to that would be using the
reflection API, which might be less performant. Not that it matters much, but
this solution is correct, even though looks a bit copy & pasty.

Oh and of course all "uint" types were completely missing in the function and
they are now added.
This commit is contained in:
nsf 2018-05-31 23:18:57 +05:00
parent aadc886acd
commit 4354d48a04
2 changed files with 90 additions and 16 deletions

View File

@ -31,6 +31,28 @@ 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:
return strconv.ParseInt(value, 10, 64)
}
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:
return strconv.ParseFloat(value, 64)
}
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 +73,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}},