hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

maps.go (4643B)

    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 any) (map[string]any, error) {
   28 	switch vv := in.(type) {
   29 	case Params:
   30 		return vv, nil
   31 	case map[string]string:
   32 		var m = map[string]any{}
   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 any) (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 any) 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 any) map[string]any {
   69 	m, _ := ToStringMapE(in)
   70 	return m
   71 }
   72 
   73 // ToStringMapStringE converts in to map[string]string.
   74 func ToStringMapStringE(in any) (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 any) map[string]string {
   84 	m, _ := ToStringMapStringE(in)
   85 	return m
   86 }
   87 
   88 // ToStringMapBool converts in to bool.
   89 func ToStringMapBool(in any) 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 any) ([]map[string]any, error) {
   96 	switch v := in.(type) {
   97 	case []map[string]any:
   98 		return v, nil
   99 	case []any:
  100 		var s []map[string]any
  101 		for _, entry := range v {
  102 			if vv, ok := entry.(map[string]any); 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 // LookupEqualFold finds key in m with case insensitive equality checks.
  113 func LookupEqualFold[T any | string](m map[string]T, key string) (T, bool) {
  114 	if v, found := m[key]; found {
  115 		return v, true
  116 	}
  117 	for k, v := range m {
  118 		if strings.EqualFold(k, key) {
  119 			return v, true
  120 		}
  121 	}
  122 	var s T
  123 	return s, false
  124 }
  125 
  126 type keyRename struct {
  127 	pattern glob.Glob
  128 	newKey  string
  129 }
  130 
  131 // KeyRenamer supports renaming of keys in a map.
  132 type KeyRenamer struct {
  133 	renames []keyRename
  134 }
  135 
  136 // NewKeyRenamer creates a new KeyRenamer given a list of pattern and new key
  137 // value pairs.
  138 func NewKeyRenamer(patternKeys ...string) (KeyRenamer, error) {
  139 	var renames []keyRename
  140 	for i := 0; i < len(patternKeys); i += 2 {
  141 		g, err := glob.Compile(strings.ToLower(patternKeys[i]), '/')
  142 		if err != nil {
  143 			return KeyRenamer{}, err
  144 		}
  145 		renames = append(renames, keyRename{pattern: g, newKey: patternKeys[i+1]})
  146 	}
  147 
  148 	return KeyRenamer{renames: renames}, nil
  149 }
  150 
  151 func (r KeyRenamer) getNewKey(keyPath string) string {
  152 	for _, matcher := range r.renames {
  153 		if matcher.pattern.Match(keyPath) {
  154 			return matcher.newKey
  155 		}
  156 	}
  157 
  158 	return ""
  159 }
  160 
  161 // Rename renames the keys in the given map according
  162 // to the patterns in the current KeyRenamer.
  163 func (r KeyRenamer) Rename(m map[string]any) {
  164 	r.renamePath("", m)
  165 }
  166 
  167 func (KeyRenamer) keyPath(k1, k2 string) string {
  168 	k1, k2 = strings.ToLower(k1), strings.ToLower(k2)
  169 	if k1 == "" {
  170 		return k2
  171 	}
  172 	return k1 + "/" + k2
  173 }
  174 
  175 func (r KeyRenamer) renamePath(parentKeyPath string, m map[string]any) {
  176 	for key, val := range m {
  177 		keyPath := r.keyPath(parentKeyPath, key)
  178 		switch val.(type) {
  179 		case map[any]any:
  180 			val = cast.ToStringMap(val)
  181 			r.renamePath(keyPath, val.(map[string]any))
  182 		case map[string]any:
  183 			r.renamePath(keyPath, val.(map[string]any))
  184 		}
  185 
  186 		newKey := r.getNewKey(keyPath)
  187 
  188 		if newKey != "" {
  189 			delete(m, key)
  190 			m[newKey] = val
  191 		}
  192 	}
  193 }