github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/common/maps/maps.go (about) 1 // Copyright 2018 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package maps 15 16 import ( 17 "fmt" 18 "strings" 19 20 "github.com/gohugoio/hugo/common/types" 21 22 "github.com/gobwas/glob" 23 "github.com/spf13/cast" 24 ) 25 26 // ToStringMapE converts in to map[string]interface{}. 27 func ToStringMapE(in interface{}) (map[string]interface{}, error) { 28 switch vv := in.(type) { 29 case Params: 30 return vv, nil 31 case map[string]string: 32 var m = map[string]interface{}{} 33 for k, v := range vv { 34 m[k] = v 35 } 36 return m, nil 37 38 default: 39 return cast.ToStringMapE(in) 40 } 41 } 42 43 // ToParamsAndPrepare converts in to Params and prepares it for use. 44 // If in is nil, an empty map is returned. 45 // See PrepareParams. 46 func ToParamsAndPrepare(in interface{}) (Params, bool) { 47 if types.IsNil(in) { 48 return Params{}, true 49 } 50 m, err := ToStringMapE(in) 51 if err != nil { 52 return nil, false 53 } 54 PrepareParams(m) 55 return m, true 56 } 57 58 // MustToParamsAndPrepare calls ToParamsAndPrepare and panics if it fails. 59 func MustToParamsAndPrepare(in interface{}) Params { 60 if p, ok := ToParamsAndPrepare(in); ok { 61 return p 62 } else { 63 panic(fmt.Sprintf("cannot convert %T to maps.Params", in)) 64 } 65 } 66 67 // ToStringMap converts in to map[string]interface{}. 68 func ToStringMap(in interface{}) map[string]interface{} { 69 m, _ := ToStringMapE(in) 70 return m 71 } 72 73 // ToStringMapStringE converts in to map[string]string. 74 func ToStringMapStringE(in interface{}) (map[string]string, error) { 75 m, err := ToStringMapE(in) 76 if err != nil { 77 return nil, err 78 } 79 return cast.ToStringMapStringE(m) 80 } 81 82 // ToStringMapString converts in to map[string]string. 83 func ToStringMapString(in interface{}) map[string]string { 84 m, _ := ToStringMapStringE(in) 85 return m 86 } 87 88 // ToStringMapBool converts in to bool. 89 func ToStringMapBool(in interface{}) map[string]bool { 90 m, _ := ToStringMapE(in) 91 return cast.ToStringMapBool(m) 92 } 93 94 // ToSliceStringMap converts in to []map[string]interface{}. 95 func ToSliceStringMap(in interface{}) ([]map[string]interface{}, error) { 96 switch v := in.(type) { 97 case []map[string]interface{}: 98 return v, nil 99 case []interface{}: 100 var s []map[string]interface{} 101 for _, entry := range v { 102 if vv, ok := entry.(map[string]interface{}); ok { 103 s = append(s, vv) 104 } 105 } 106 return s, nil 107 default: 108 return nil, fmt.Errorf("unable to cast %#v of type %T to []map[string]interface{}", in, in) 109 } 110 } 111 112 type keyRename struct { 113 pattern glob.Glob 114 newKey string 115 } 116 117 // KeyRenamer supports renaming of keys in a map. 118 type KeyRenamer struct { 119 renames []keyRename 120 } 121 122 // NewKeyRenamer creates a new KeyRenamer given a list of pattern and new key 123 // value pairs. 124 func NewKeyRenamer(patternKeys ...string) (KeyRenamer, error) { 125 var renames []keyRename 126 for i := 0; i < len(patternKeys); i += 2 { 127 g, err := glob.Compile(strings.ToLower(patternKeys[i]), '/') 128 if err != nil { 129 return KeyRenamer{}, err 130 } 131 renames = append(renames, keyRename{pattern: g, newKey: patternKeys[i+1]}) 132 } 133 134 return KeyRenamer{renames: renames}, nil 135 } 136 137 func (r KeyRenamer) getNewKey(keyPath string) string { 138 for _, matcher := range r.renames { 139 if matcher.pattern.Match(keyPath) { 140 return matcher.newKey 141 } 142 } 143 144 return "" 145 } 146 147 // Rename renames the keys in the given map according 148 // to the patterns in the current KeyRenamer. 149 func (r KeyRenamer) Rename(m map[string]interface{}) { 150 r.renamePath("", m) 151 } 152 153 func (KeyRenamer) keyPath(k1, k2 string) string { 154 k1, k2 = strings.ToLower(k1), strings.ToLower(k2) 155 if k1 == "" { 156 return k2 157 } 158 return k1 + "/" + k2 159 } 160 161 func (r KeyRenamer) renamePath(parentKeyPath string, m map[string]interface{}) { 162 for key, val := range m { 163 keyPath := r.keyPath(parentKeyPath, key) 164 switch val.(type) { 165 case map[interface{}]interface{}: 166 val = cast.ToStringMap(val) 167 r.renamePath(keyPath, val.(map[string]interface{})) 168 case map[string]interface{}: 169 r.renamePath(keyPath, val.(map[string]interface{})) 170 } 171 172 newKey := r.getNewKey(keyPath) 173 174 if newKey != "" { 175 delete(m, key) 176 m[newKey] = val 177 } 178 } 179 }