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