diff options
author | Bjørn Erik Pedersen <[email protected]> | 2019-11-21 21:59:38 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2019-11-22 18:41:50 +0100 |
commit | a3fe5e5e35f311f22b6b4fc38abfcf64cd2c7d6f (patch) | |
tree | 06cf1f647ae026b4fb3053c85370c2b203c7a089 /common/maps | |
parent | cd07e6d57b158a76f812e8c4c9567dbc84f57939 (diff) | |
download | hugo-a3fe5e5e35f311f22b6b4fc38abfcf64cd2c7d6f.tar.gz hugo-a3fe5e5e35f311f22b6b4fc38abfcf64cd2c7d6f.zip |
Fix Params case handling in the index, sort and where func
This means that you can now do:
```
{{ range where .Site.Pages "Params.MYPARAM" "foo" }}
```
Diffstat (limited to 'common/maps')
-rw-r--r-- | common/maps/maps.go | 31 | ||||
-rw-r--r-- | common/maps/maps_test.go | 24 | ||||
-rw-r--r-- | common/maps/params.go | 109 | ||||
-rw-r--r-- | common/maps/params_test.go | 2 |
4 files changed, 100 insertions, 66 deletions
diff --git a/common/maps/maps.go b/common/maps/maps.go index e0d4f964d..8b42ca764 100644 --- a/common/maps/maps.go +++ b/common/maps/maps.go @@ -25,26 +25,45 @@ import ( // recursively. // Notes: // * This will modify the map given. -// * Any nested map[interface{}]interface{} will be converted to map[string]interface{}. -func ToLower(m map[string]interface{}) { +// * Any nested map[interface{}]interface{} will be converted to Params. +func ToLower(m Params) { for k, v := range m { + var retyped bool switch v.(type) { case map[interface{}]interface{}: - v = cast.ToStringMap(v) - ToLower(v.(map[string]interface{})) + var p Params = cast.ToStringMap(v) + v = p + ToLower(p) + retyped = true case map[string]interface{}: - ToLower(v.(map[string]interface{})) + var p Params = v.(map[string]interface{}) + v = p + ToLower(p) + retyped = true } lKey := strings.ToLower(k) - if k != lKey { + if retyped || k != lKey { delete(m, k) m[lKey] = v } + } +} +func ToStringMapE(in interface{}) (map[string]interface{}, error) { + switch in.(type) { + case Params: + return in.(Params), nil + default: + return cast.ToStringMapE(in) } } +func ToStringMap(in interface{}) map[string]interface{} { + m, _ := ToStringMapE(in) + return m +} + type keyRename struct { pattern glob.Glob newKey string diff --git a/common/maps/maps_test.go b/common/maps/maps_test.go index 8b0aa5eb9..6e4947adb 100644 --- a/common/maps/maps_test.go +++ b/common/maps/maps_test.go @@ -14,6 +14,7 @@ package maps import ( + "fmt" "reflect" "testing" @@ -21,7 +22,6 @@ import ( ) func TestToLower(t *testing.T) { - tests := []struct { input map[string]interface{} expected map[string]interface{} @@ -30,7 +30,7 @@ func TestToLower(t *testing.T) { map[string]interface{}{ "abC": 32, }, - map[string]interface{}{ + Params{ "abc": 32, }, }, @@ -48,16 +48,16 @@ func TestToLower(t *testing.T) { "J": 25, }, }, - map[string]interface{}{ + Params{ "abc": 32, - "def": map[string]interface{}{ + "def": Params{ "23": "A value", - "24": map[string]interface{}{ + "24": Params{ "abcde": "A value", "efghi": "Another value", }, }, - "ghi": map[string]interface{}{ + "ghi": Params{ "j": 25, }, }, @@ -65,11 +65,13 @@ func TestToLower(t *testing.T) { } for i, test := range tests { - // ToLower modifies input. - ToLower(test.input) - if !reflect.DeepEqual(test.expected, test.input) { - t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input) - } + t.Run(fmt.Sprint(i), func(t *testing.T) { + // ToLower modifies input. + ToLower(test.input) + if !reflect.DeepEqual(test.expected, test.input) { + t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input) + } + }) } } diff --git a/common/maps/params.go b/common/maps/params.go index 2d62ad752..ecb63d7a5 100644 --- a/common/maps/params.go +++ b/common/maps/params.go @@ -19,76 +19,89 @@ import ( "github.com/spf13/cast" ) +// Params is a map where all keys are lower case. +type Params map[string]interface{} + +// Get does a lower case and nested search in this map. +// It will return nil if none found. +func (p Params) Get(indices ...string) interface{} { + v, _, _ := getNested(p, indices) + return v +} + +func getNested(m map[string]interface{}, indices []string) (interface{}, string, map[string]interface{}) { + if len(indices) == 0 { + return nil, "", nil + } + + first := indices[0] + v, found := m[strings.ToLower(cast.ToString(first))] + if !found { + return nil, "", nil + } + + if len(indices) == 1 { + return v, first, m + } + + switch m2 := v.(type) { + case Params: + return getNested(m2, indices[1:]) + case map[string]interface{}: + return getNested(m2, indices[1:]) + default: + return nil, "", nil + } +} + // GetNestedParam gets the first match of the keyStr in the candidates given. // It will first try the exact match and then try to find it as a nested map value, // using the given separator, e.g. "mymap.name". // It assumes that all the maps given have lower cased keys. -func GetNestedParam(keyStr, separator string, candidates ...map[string]interface{}) (interface{}, error) { +func GetNestedParam(keyStr, separator string, candidates ...Params) (interface{}, error) { keyStr = strings.ToLower(keyStr) - lookupFn := func(key string) interface{} { - for _, m := range candidates { - if v, ok := m[key]; ok { - return v - } + // Try exact match first + for _, m := range candidates { + if v, ok := m[keyStr]; ok { + return v, nil } - - return nil - } - - v, _, _, err := GetNestedParamFn(keyStr, separator, lookupFn) - return v, err -} - -func GetNestedParamFn(keyStr, separator string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) { - result, _ := traverseDirectParams(keyStr, lookupFn) - if result != nil { - return result, keyStr, nil, nil } keySegments := strings.Split(keyStr, separator) - if len(keySegments) == 1 { - return nil, keyStr, nil, nil + for _, m := range candidates { + if v := m.Get(keySegments...); v != nil { + return v, nil + } } - return traverseNestedParams(keySegments, lookupFn) -} + return nil, nil -func traverseDirectParams(keyStr string, lookupFn func(key string) interface{}) (interface{}, error) { - return lookupFn(keyStr), nil } -func traverseNestedParams(keySegments []string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) { - firstKey, rest := keySegments[0], keySegments[1:] - result := lookupFn(firstKey) - if result == nil || len(rest) == 0 { - return result, firstKey, nil, nil +func GetNestedParamFn(keyStr, separator string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) { + keySegments := strings.Split(strings.ToLower(keyStr), separator) + if len(keySegments) == 0 { + return nil, "", nil, nil } - switch m := result.(type) { - case map[string]interface{}: - v, key, owner := traverseParams(rest, m) - return v, key, owner, nil - default: + first := lookupFn(keySegments[0]) + if first == nil { return nil, "", nil, nil } -} - -func traverseParams(keys []string, m map[string]interface{}) (interface{}, string, map[string]interface{}) { - // Shift first element off. - firstKey, rest := keys[0], keys[1:] - result := m[firstKey] - // No point in continuing here. - if result == nil { - return result, "", nil + if len(keySegments) == 1 { + return first, keySegments[0], nil, nil } - if len(rest) == 0 { - // That was the last key. - return result, firstKey, m + switch m := first.(type) { + case map[string]interface{}: + v, key, owner := getNested(m, keySegments[1:]) + return v, key, owner, nil + case Params: + v, key, owner := getNested(m, keySegments[1:]) + return v, key, owner, nil } - // That was not the last key. - return traverseParams(rest, cast.ToStringMap(result)) + return nil, "", nil, nil } diff --git a/common/maps/params_test.go b/common/maps/params_test.go index 6477de6f4..8016a8bd6 100644 --- a/common/maps/params_test.go +++ b/common/maps/params_test.go @@ -35,7 +35,7 @@ func TestGetNestedParam(t *testing.T) { c := qt.New(t) - must := func(keyStr, separator string, candidates ...map[string]interface{}) interface{} { + must := func(keyStr, separator string, candidates ...Params) interface{} { v, err := GetNestedParam(keyStr, separator, candidates...) c.Assert(err, qt.IsNil) return v |