hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

glob.go (3496B)

    1 // Copyright 2021 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 glob
   15 
   16 import (
   17 	"os"
   18 	"path"
   19 	"path/filepath"
   20 	"runtime"
   21 	"strings"
   22 	"sync"
   23 
   24 	"github.com/gobwas/glob"
   25 	"github.com/gobwas/glob/syntax"
   26 )
   27 
   28 const filepathSeparator = string(os.PathSeparator)
   29 
   30 var (
   31 	isWindows        = runtime.GOOS == "windows"
   32 	defaultGlobCache = &globCache{
   33 		isCaseSensitive: false,
   34 		isWindows:       isWindows,
   35 		cache:           make(map[string]globErr),
   36 	}
   37 
   38 	filenamesGlobCache = &globCache{
   39 		isCaseSensitive: false, // As long as the search strings are all lower case, this does not allocate.
   40 		isWindows:       isWindows,
   41 		cache:           make(map[string]globErr),
   42 	}
   43 )
   44 
   45 type globErr struct {
   46 	glob glob.Glob
   47 	err  error
   48 }
   49 
   50 type globCache struct {
   51 	// Config
   52 	isCaseSensitive bool
   53 	isWindows       bool
   54 
   55 	// Cache
   56 	sync.RWMutex
   57 	cache map[string]globErr
   58 }
   59 
   60 func (gc *globCache) GetGlob(pattern string) (glob.Glob, error) {
   61 	var eg globErr
   62 
   63 	gc.RLock()
   64 	var found bool
   65 	eg, found = gc.cache[pattern]
   66 	gc.RUnlock()
   67 	if found {
   68 		return eg.glob, eg.err
   69 	}
   70 
   71 	var g glob.Glob
   72 	var err error
   73 
   74 	pattern = filepath.ToSlash(pattern)
   75 
   76 	if gc.isCaseSensitive {
   77 		g, err = glob.Compile(pattern, '/')
   78 	} else {
   79 		g, err = glob.Compile(strings.ToLower(pattern), '/')
   80 
   81 	}
   82 
   83 	eg = globErr{
   84 		globDecorator{
   85 			g:               g,
   86 			isCaseSensitive: gc.isCaseSensitive,
   87 			isWindows:       gc.isWindows},
   88 		err,
   89 	}
   90 
   91 	gc.Lock()
   92 	gc.cache[pattern] = eg
   93 	gc.Unlock()
   94 
   95 	return eg.glob, eg.err
   96 }
   97 
   98 type globDecorator struct {
   99 	// Whether both pattern and the strings to match will be matched
  100 	// by their original case.
  101 	isCaseSensitive bool
  102 
  103 	// On Windows we may get filenames with Windows slashes to match,
  104 	// which wee need to normalize.
  105 	isWindows bool
  106 
  107 	g glob.Glob
  108 }
  109 
  110 func (g globDecorator) Match(s string) bool {
  111 	if g.isWindows {
  112 		s = filepath.ToSlash(s)
  113 	}
  114 	if !g.isCaseSensitive {
  115 		s = strings.ToLower(s)
  116 	}
  117 	return g.g.Match(s)
  118 }
  119 
  120 func GetGlob(pattern string) (glob.Glob, error) {
  121 	return defaultGlobCache.GetGlob(pattern)
  122 }
  123 
  124 func NormalizePath(p string) string {
  125 	return strings.Trim(path.Clean(filepath.ToSlash(strings.ToLower(p))), "/.")
  126 }
  127 
  128 // ResolveRootDir takes a normalized path on the form "assets/**.json" and
  129 // determines any root dir, i.e. any start path without any wildcards.
  130 func ResolveRootDir(p string) string {
  131 	parts := strings.Split(path.Dir(p), "/")
  132 	var roots []string
  133 	for _, part := range parts {
  134 		if HasGlobChar(part) {
  135 			break
  136 		}
  137 		roots = append(roots, part)
  138 	}
  139 
  140 	if len(roots) == 0 {
  141 		return ""
  142 	}
  143 
  144 	return strings.Join(roots, "/")
  145 }
  146 
  147 // FilterGlobParts removes any string with glob wildcard.
  148 func FilterGlobParts(a []string) []string {
  149 	b := a[:0]
  150 	for _, x := range a {
  151 		if !HasGlobChar(x) {
  152 			b = append(b, x)
  153 		}
  154 	}
  155 	return b
  156 }
  157 
  158 // HasGlobChar returns whether s contains any glob wildcards.
  159 func HasGlobChar(s string) bool {
  160 	for i := 0; i < len(s); i++ {
  161 		if syntax.Special(s[i]) {
  162 			return true
  163 		}
  164 	}
  165 	return false
  166 }