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  }