github.com/rn2dy/hugo@v0.47.1/resource/resource_metadata.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 resource
    15  
    16  import (
    17  	"fmt"
    18  	"strconv"
    19  
    20  	"github.com/spf13/cast"
    21  
    22  	"strings"
    23  
    24  	"github.com/gohugoio/hugo/common/maps"
    25  )
    26  
    27  var (
    28  	_ metaAssigner = (*genericResource)(nil)
    29  )
    30  
    31  // metaAssigner allows updating metadata in resources that supports it.
    32  type metaAssigner interface {
    33  	setTitle(title string)
    34  	setName(name string)
    35  	updateParams(params map[string]interface{})
    36  }
    37  
    38  const counterPlaceHolder = ":counter"
    39  
    40  // AssignMetadata assigns the given metadata to those resources that supports updates
    41  // and matching by wildcard given in `src` using `filepath.Match` with lower cased values.
    42  // This assignment is additive, but the most specific match needs to be first.
    43  // The `name` and `title` metadata field support shell-matched collection it got a match in.
    44  // See https://golang.org/pkg/path/#Match
    45  func AssignMetadata(metadata []map[string]interface{}, resources ...Resource) error {
    46  
    47  	counters := make(map[string]int)
    48  
    49  	for _, r := range resources {
    50  		if _, ok := r.(metaAssigner); !ok {
    51  			continue
    52  		}
    53  
    54  		var (
    55  			nameSet, titleSet                   bool
    56  			nameCounter, titleCounter           = 0, 0
    57  			nameCounterFound, titleCounterFound bool
    58  			resourceSrcKey                      = strings.ToLower(r.Name())
    59  		)
    60  
    61  		ma := r.(metaAssigner)
    62  		for _, meta := range metadata {
    63  			src, found := meta["src"]
    64  			if !found {
    65  				return fmt.Errorf("missing 'src' in metadata for resource")
    66  			}
    67  
    68  			srcKey := strings.ToLower(cast.ToString(src))
    69  
    70  			glob, err := getGlob(srcKey)
    71  			if err != nil {
    72  				return fmt.Errorf("failed to match resource with metadata: %s", err)
    73  			}
    74  
    75  			match := glob.Match(resourceSrcKey)
    76  
    77  			if match {
    78  				if !nameSet {
    79  					name, found := meta["name"]
    80  					if found {
    81  						name := cast.ToString(name)
    82  						if !nameCounterFound {
    83  							nameCounterFound = strings.Contains(name, counterPlaceHolder)
    84  						}
    85  						if nameCounterFound && nameCounter == 0 {
    86  							counterKey := "name_" + srcKey
    87  							nameCounter = counters[counterKey] + 1
    88  							counters[counterKey] = nameCounter
    89  						}
    90  
    91  						ma.setName(replaceResourcePlaceholders(name, nameCounter))
    92  						nameSet = true
    93  					}
    94  				}
    95  
    96  				if !titleSet {
    97  					title, found := meta["title"]
    98  					if found {
    99  						title := cast.ToString(title)
   100  						if !titleCounterFound {
   101  							titleCounterFound = strings.Contains(title, counterPlaceHolder)
   102  						}
   103  						if titleCounterFound && titleCounter == 0 {
   104  							counterKey := "title_" + srcKey
   105  							titleCounter = counters[counterKey] + 1
   106  							counters[counterKey] = titleCounter
   107  						}
   108  						ma.setTitle((replaceResourcePlaceholders(title, titleCounter)))
   109  						titleSet = true
   110  					}
   111  				}
   112  
   113  				params, found := meta["params"]
   114  				if found {
   115  					m := cast.ToStringMap(params)
   116  					// Needed for case insensitive fetching of params values
   117  					maps.ToLower(m)
   118  					ma.updateParams(m)
   119  				}
   120  			}
   121  		}
   122  	}
   123  
   124  	return nil
   125  }
   126  
   127  func replaceResourcePlaceholders(in string, counter int) string {
   128  	return strings.Replace(in, counterPlaceHolder, strconv.Itoa(counter), -1)
   129  }