github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/hugofs/glob/glob.go (about) 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 isWindows: isWindows, 34 cache: make(map[string]globErr), 35 } 36 ) 37 38 type globErr struct { 39 glob glob.Glob 40 err error 41 } 42 43 type globCache struct { 44 // Config 45 isWindows bool 46 47 // Cache 48 sync.RWMutex 49 cache map[string]globErr 50 } 51 52 func (gc *globCache) GetGlob(pattern string) (glob.Glob, error) { 53 var eg globErr 54 55 gc.RLock() 56 var found bool 57 eg, found = gc.cache[pattern] 58 gc.RUnlock() 59 if found { 60 return eg.glob, eg.err 61 } 62 63 var g glob.Glob 64 var err error 65 66 pattern = filepath.ToSlash(pattern) 67 g, err = glob.Compile(strings.ToLower(pattern), '/') 68 69 eg = globErr{ 70 globDecorator{ 71 g: g, 72 isWindows: gc.isWindows}, 73 err, 74 } 75 76 gc.Lock() 77 gc.cache[pattern] = eg 78 gc.Unlock() 79 80 return eg.glob, eg.err 81 } 82 83 // Or creates a new Glob from the given globs. 84 func Or(globs ...glob.Glob) glob.Glob { 85 return globSlice{globs: globs} 86 } 87 88 // MatchesFunc is a convenience type to create a glob.Glob from a function. 89 type MatchesFunc func(s string) bool 90 91 func (m MatchesFunc) Match(s string) bool { 92 return m(s) 93 } 94 95 type globSlice struct { 96 globs []glob.Glob 97 } 98 99 func (g globSlice) Match(s string) bool { 100 for _, g := range g.globs { 101 if g.Match(s) { 102 return true 103 } 104 } 105 return false 106 } 107 108 type globDecorator struct { 109 // On Windows we may get filenames with Windows slashes to match, 110 // which we need to normalize. 111 isWindows bool 112 113 g glob.Glob 114 } 115 116 func (g globDecorator) Match(s string) bool { 117 if g.isWindows { 118 s = filepath.ToSlash(s) 119 } 120 s = strings.ToLower(s) 121 return g.g.Match(s) 122 } 123 124 type globDecoratorDouble struct { 125 lowerCase glob.Glob 126 originalCase glob.Glob 127 } 128 129 func (g globDecoratorDouble) Match(s string) bool { 130 return g.lowerCase.Match(s) || g.originalCase.Match(s) 131 } 132 133 func GetGlob(pattern string) (glob.Glob, error) { 134 return defaultGlobCache.GetGlob(pattern) 135 } 136 137 func NormalizePath(p string) string { 138 return strings.ToLower(NormalizePathNoLower(p)) 139 } 140 141 func NormalizePathNoLower(p string) string { 142 return strings.Trim(path.Clean(filepath.ToSlash(p)), "/.") 143 } 144 145 // ResolveRootDir takes a normalized path on the form "assets/**.json" and 146 // determines any root dir, i.e. any start path without any wildcards. 147 func ResolveRootDir(p string) string { 148 parts := strings.Split(path.Dir(p), "/") 149 var roots []string 150 for _, part := range parts { 151 if HasGlobChar(part) { 152 break 153 } 154 roots = append(roots, part) 155 } 156 157 if len(roots) == 0 { 158 return "" 159 } 160 161 return strings.Join(roots, "/") 162 } 163 164 // FilterGlobParts removes any string with glob wildcard. 165 func FilterGlobParts(a []string) []string { 166 b := a[:0] 167 for _, x := range a { 168 if !HasGlobChar(x) { 169 b = append(b, x) 170 } 171 } 172 return b 173 } 174 175 // HasGlobChar returns whether s contains any glob wildcards. 176 func HasGlobChar(s string) bool { 177 for i := 0; i < len(s); i++ { 178 if syntax.Special(s[i]) { 179 return true 180 } 181 } 182 return false 183 }