github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/resources/page/page_matcher.go (about) 1 // Copyright 2020 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 page 15 16 import ( 17 "path/filepath" 18 "strings" 19 20 "github.com/gohugoio/hugo/common/maps" 21 "github.com/gohugoio/hugo/hugofs/glob" 22 "github.com/mitchellh/mapstructure" 23 "github.com/pkg/errors" 24 ) 25 26 // A PageMatcher can be used to match a Page with Glob patterns. 27 // Note that the pattern matching is case insensitive. 28 type PageMatcher struct { 29 // A Glob pattern matching the content path below /content. 30 // Expects Unix-styled slashes. 31 // Note that this is the virtual path, so it starts at the mount root 32 // with a leading "/". 33 Path string 34 35 // A Glob pattern matching the Page's Kind(s), e.g. "{home,section}" 36 Kind string 37 38 // A Glob pattern matching the Page's language, e.g. "{en,sv}". 39 Lang string 40 } 41 42 // Matches returns whether p matches this matcher. 43 func (m PageMatcher) Matches(p Page) bool { 44 if m.Kind != "" { 45 g, err := glob.GetGlob(m.Kind) 46 if err == nil && !g.Match(p.Kind()) { 47 return false 48 } 49 } 50 51 if m.Lang != "" { 52 g, err := glob.GetGlob(m.Lang) 53 if err == nil && !g.Match(p.Lang()) { 54 return false 55 } 56 } 57 58 if m.Path != "" { 59 g, err := glob.GetGlob(m.Path) 60 // TODO(bep) Path() vs filepath vs leading slash. 61 p := strings.ToLower(filepath.ToSlash(p.Pathc())) 62 if !(strings.HasPrefix(p, "/")) { 63 p = "/" + p 64 } 65 if err == nil && !g.Match(p) { 66 return false 67 } 68 } 69 70 return true 71 } 72 73 // DecodeCascade decodes in which could be eiter a map or a slice of maps. 74 func DecodeCascade(in interface{}) (map[PageMatcher]maps.Params, error) { 75 m, err := maps.ToSliceStringMap(in) 76 if err != nil { 77 return map[PageMatcher]maps.Params{ 78 {}: maps.ToStringMap(in), 79 }, nil 80 } 81 82 cascade := make(map[PageMatcher]maps.Params) 83 84 for _, vv := range m { 85 var m PageMatcher 86 if mv, found := vv["_target"]; found { 87 err := DecodePageMatcher(mv, &m) 88 if err != nil { 89 return nil, err 90 } 91 } 92 c, found := cascade[m] 93 if found { 94 // Merge 95 for k, v := range vv { 96 if _, found := c[k]; !found { 97 c[k] = v 98 } 99 } 100 } else { 101 cascade[m] = vv 102 } 103 } 104 105 return cascade, nil 106 } 107 108 // DecodePageMatcher decodes m into v. 109 func DecodePageMatcher(m interface{}, v *PageMatcher) error { 110 if err := mapstructure.WeakDecode(m, v); err != nil { 111 return err 112 } 113 114 v.Kind = strings.ToLower(v.Kind) 115 if v.Kind != "" { 116 g, _ := glob.GetGlob(v.Kind) 117 found := false 118 for _, k := range kindMap { 119 if g.Match(k) { 120 found = true 121 break 122 } 123 } 124 if !found { 125 return errors.Errorf("%q did not match a valid Page Kind", v.Kind) 126 } 127 } 128 129 v.Path = filepath.ToSlash(strings.ToLower(v.Path)) 130 131 return nil 132 }