summaryrefslogtreecommitdiffstats
path: root/common/maps/maps.go
diff options
context:
space:
mode:
Diffstat (limited to 'common/maps/maps.go')
-rw-r--r--common/maps/maps.go135
1 files changed, 135 insertions, 0 deletions
diff --git a/common/maps/maps.go b/common/maps/maps.go
new file mode 100644
index 000000000..8b42ca764
--- /dev/null
+++ b/common/maps/maps.go
@@ -0,0 +1,135 @@
+// Copyright 2018 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package maps
+
+import (
+ "strings"
+
+ "github.com/gobwas/glob"
+
+ "github.com/spf13/cast"
+)
+
+// ToLower makes all the keys in the given map lower cased and will do so
+// recursively.
+// Notes:
+// * This will modify the map given.
+// * 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{}:
+ var p Params = cast.ToStringMap(v)
+ v = p
+ ToLower(p)
+ retyped = true
+ case map[string]interface{}:
+ var p Params = v.(map[string]interface{})
+ v = p
+ ToLower(p)
+ retyped = true
+ }
+
+ lKey := strings.ToLower(k)
+ 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
+}
+
+// KeyRenamer supports renaming of keys in a map.
+type KeyRenamer struct {
+ renames []keyRename
+}
+
+// NewKeyRenamer creates a new KeyRenamer given a list of pattern and new key
+// value pairs.
+func NewKeyRenamer(patternKeys ...string) (KeyRenamer, error) {
+ var renames []keyRename
+ for i := 0; i < len(patternKeys); i += 2 {
+ g, err := glob.Compile(strings.ToLower(patternKeys[i]), '/')
+ if err != nil {
+ return KeyRenamer{}, err
+ }
+ renames = append(renames, keyRename{pattern: g, newKey: patternKeys[i+1]})
+ }
+
+ return KeyRenamer{renames: renames}, nil
+}
+
+func (r KeyRenamer) getNewKey(keyPath string) string {
+ for _, matcher := range r.renames {
+ if matcher.pattern.Match(keyPath) {
+ return matcher.newKey
+ }
+ }
+
+ return ""
+}
+
+// Rename renames the keys in the given map according
+// to the patterns in the current KeyRenamer.
+func (r KeyRenamer) Rename(m map[string]interface{}) {
+ r.renamePath("", m)
+}
+
+func (KeyRenamer) keyPath(k1, k2 string) string {
+ k1, k2 = strings.ToLower(k1), strings.ToLower(k2)
+ if k1 == "" {
+ return k2
+ } else {
+ return k1 + "/" + k2
+ }
+}
+
+func (r KeyRenamer) renamePath(parentKeyPath string, m map[string]interface{}) {
+ for key, val := range m {
+ keyPath := r.keyPath(parentKeyPath, key)
+ switch val.(type) {
+ case map[interface{}]interface{}:
+ val = cast.ToStringMap(val)
+ r.renamePath(keyPath, val.(map[string]interface{}))
+ case map[string]interface{}:
+ r.renamePath(keyPath, val.(map[string]interface{}))
+ }
+
+ newKey := r.getNewKey(keyPath)
+
+ if newKey != "" {
+ delete(m, key)
+ m[newKey] = val
+ }
+ }
+}