mirror of
https://github.com/danog/liquid.git
synced 2024-11-30 08:18:59 +01:00
Convert MapSlice -> map
This commit is contained in:
parent
bb24f32cbd
commit
1a12f12f1d
@ -70,6 +70,34 @@ func Convert(value interface{}, typ reflect.Type) (interface{}, error) { // noli
|
||||
case reflect.Map:
|
||||
et := typ.Elem()
|
||||
result := reflect.MakeMap(typ)
|
||||
if ms, ok := value.(yaml.MapSlice); ok {
|
||||
for _, item := range ms {
|
||||
var k, v reflect.Value
|
||||
if item.Key == nil {
|
||||
k = reflect.Zero(typ.Key())
|
||||
} else {
|
||||
kc, err := Convert(item.Key, typ.Key())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k = reflect.ValueOf(kc)
|
||||
}
|
||||
if item.Value == nil {
|
||||
v = reflect.Zero(et)
|
||||
} else {
|
||||
ec, err := Convert(item.Value, et)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v = reflect.ValueOf(ec)
|
||||
}
|
||||
result.SetMapIndex(k, v)
|
||||
}
|
||||
return result.Interface(), nil
|
||||
}
|
||||
if rv.Kind() != reflect.Map {
|
||||
return nil, conversionError("", value, typ)
|
||||
}
|
||||
for _, key := range rv.MapKeys() {
|
||||
if typ.Key().Kind() == reflect.String {
|
||||
key = reflect.ValueOf(fmt.Sprint(key))
|
||||
@ -83,7 +111,7 @@ func Convert(value interface{}, typ reflect.Type) (interface{}, error) { // noli
|
||||
ev = reflect.ValueOf(fmt.Sprint(ev))
|
||||
}
|
||||
if !ev.Type().ConvertibleTo(et) {
|
||||
return nil, conversionError("map value", ev, et)
|
||||
return nil, conversionError("map element", ev, et)
|
||||
}
|
||||
result.SetMapIndex(key, ev.Convert(et))
|
||||
}
|
||||
@ -93,7 +121,6 @@ func Convert(value interface{}, typ reflect.Type) (interface{}, error) { // noli
|
||||
if ms, ok := value.(yaml.MapSlice); ok {
|
||||
result := reflect.MakeSlice(typ, 0, rv.Len())
|
||||
for _, item := range ms {
|
||||
// TODO something more nuanced
|
||||
if item.Value == nil {
|
||||
if et.Kind() >= reflect.Array {
|
||||
ev := reflect.Zero(et)
|
||||
@ -106,7 +133,7 @@ func Convert(value interface{}, typ reflect.Type) (interface{}, error) { // noli
|
||||
ev = reflect.ValueOf(fmt.Sprint(ev))
|
||||
}
|
||||
if !ev.Type().ConvertibleTo(et) {
|
||||
return nil, conversionError("map value", ev, et)
|
||||
return nil, conversionError("slice element", ev, et)
|
||||
}
|
||||
result = reflect.Append(result, ev.Convert(et))
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -16,43 +18,52 @@ func (c redConvertible) ToLiquid() interface{} {
|
||||
return "red"
|
||||
}
|
||||
|
||||
func timeMustParse(s string) time.Time {
|
||||
t, err := time.Parse(time.RFC3339, s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
var convertTests = []struct {
|
||||
var convertProtoTests = []struct {
|
||||
value, proto, expected interface{}
|
||||
}{
|
||||
{1, 1.0, float64(1)},
|
||||
{"2", 1, int(2)},
|
||||
{"1.2", 1.0, float64(1.2)},
|
||||
{true, 1, 1},
|
||||
{false, 1, 0},
|
||||
{nil, true, false},
|
||||
{0, true, true},
|
||||
{"", true, true},
|
||||
{1, "", "1"},
|
||||
{false, "", "false"},
|
||||
{true, "", "true"},
|
||||
{"string", "", "string"},
|
||||
{[]int{1, 2}, []string{}, []string{"1", "2"}},
|
||||
// {"March 14, 2016", time.Now(), timeMustParse("2016-03-14T00:00:00Z")},
|
||||
{redConvertible{}, "", "red"},
|
||||
}
|
||||
|
||||
var convertTests = []struct {
|
||||
value, expected interface{}
|
||||
}{
|
||||
{nil, false},
|
||||
{true, 1},
|
||||
{false, 0},
|
||||
{0, true},
|
||||
{"", true},
|
||||
{1, "1"},
|
||||
{false, "false"},
|
||||
{true, "true"},
|
||||
{"string", "string"},
|
||||
{[]int{1, 2}, []interface{}{1, 2}},
|
||||
{[]interface{}{1, 2}, []int{1, 2}},
|
||||
{[]int{1, 2}, []string{"1", "2"}},
|
||||
{yaml.MapSlice{{Key: 1, Value: 1}}, []interface{}{1}},
|
||||
{yaml.MapSlice{{Key: 1, Value: 1}}, []string{"1"}},
|
||||
{yaml.MapSlice{{Key: 1, Value: "a"}}, []string{"a"}},
|
||||
{yaml.MapSlice{{Key: 1, Value: "a"}}, map[interface{}]interface{}{1: "a"}},
|
||||
{yaml.MapSlice{{Key: 1, Value: "a"}}, map[int]string{1: "a"}},
|
||||
{yaml.MapSlice{{Key: 1, Value: "a"}}, map[string]string{"1": "a"}},
|
||||
{yaml.MapSlice{{Key: "a", Value: 1}}, map[string]string{"a": "1"}},
|
||||
{yaml.MapSlice{{Key: "a", Value: nil}}, map[string]interface{}{"a": nil}},
|
||||
{yaml.MapSlice{{Key: nil, Value: 1}}, map[interface{}]string{nil: "1"}},
|
||||
// {"March 14, 2016", time.Now(), timeMustParse("2016-03-14T00:00:00Z")},
|
||||
{redConvertible{}, "red"},
|
||||
}
|
||||
|
||||
var convertErrorTests = []struct {
|
||||
value, proto, expected interface{}
|
||||
}{
|
||||
{map[string]bool{"k": true}, map[int]bool{}, "map key"},
|
||||
{map[string]string{"k": "v"}, map[string]int{}, "map value"},
|
||||
{map[interface{}]interface{}{"k": "v"}, map[string]int{}, "map value"},
|
||||
{map[string]string{"k": "v"}, map[string]int{}, "map element"},
|
||||
{map[interface{}]interface{}{"k": "v"}, map[string]int{}, "map element"},
|
||||
}
|
||||
|
||||
func TestConvert(t *testing.T) {
|
||||
for i, test := range convertTests {
|
||||
for i, test := range convertProtoTests {
|
||||
t.Run(fmt.Sprintf("%02d", i+1), func(t *testing.T) {
|
||||
typ := reflect.TypeOf(test.proto)
|
||||
name := fmt.Sprintf("Convert %#v -> %v", test.value, typ)
|
||||
@ -61,6 +72,16 @@ func TestConvert(t *testing.T) {
|
||||
require.Equalf(t, test.expected, value, name)
|
||||
})
|
||||
}
|
||||
|
||||
for i, test := range convertTests {
|
||||
t.Run(fmt.Sprintf("%02d", i+1), func(t *testing.T) {
|
||||
typ := reflect.TypeOf(test.expected)
|
||||
name := fmt.Sprintf("Convert %#v -> %v", test.value, typ)
|
||||
value, err := Convert(test.value, typ)
|
||||
require.NoErrorf(t, err, name)
|
||||
require.Equalf(t, test.expected, value, name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvert_errors(t *testing.T) {
|
||||
@ -130,3 +151,11 @@ func TestMustConvertItem(t *testing.T) {
|
||||
|
||||
require.Panics(t, func() { MustConvertItem("x", []int{}) })
|
||||
}
|
||||
|
||||
func timeMustParse(s string) time.Time {
|
||||
t, err := time.Parse(time.RFC3339, s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user