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  }