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  }