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