github.com/neohugo/neohugo@v0.123.8/common/hstrings/strings.go (about) 1 // Copyright 2024 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 hstrings 15 16 import ( 17 "fmt" 18 "regexp" 19 "strings" 20 "sync" 21 22 "github.com/neohugo/neohugo/compare" 23 ) 24 25 var _ compare.Eqer = StringEqualFold("") 26 27 // StringEqualFold is a string that implements the compare.Eqer interface and considers 28 // two strings equal if they are equal when folded to lower case. 29 // The compare.Eqer interface is used in Hugo to compare values in templates (e.g. using the eq template function). 30 type StringEqualFold string 31 32 func (s StringEqualFold) EqualFold(s2 string) bool { 33 return strings.EqualFold(string(s), s2) 34 } 35 36 func (s StringEqualFold) String() string { 37 return string(s) 38 } 39 40 func (s StringEqualFold) Eq(s2 any) bool { 41 switch ss := s2.(type) { 42 case string: 43 return s.EqualFold(ss) 44 case fmt.Stringer: 45 return s.EqualFold(ss.String()) 46 } 47 48 return false 49 } 50 51 // EqualAny returns whether a string is equal to any of the given strings. 52 func EqualAny(a string, b ...string) bool { 53 for _, s := range b { 54 if a == s { 55 return true 56 } 57 } 58 return false 59 } 60 61 // regexpCache represents a cache of regexp objects protected by a mutex. 62 type regexpCache struct { 63 mu sync.RWMutex 64 re map[string]*regexp.Regexp 65 } 66 67 func (rc *regexpCache) getOrCompileRegexp(pattern string) (re *regexp.Regexp, err error) { 68 var ok bool 69 70 if re, ok = rc.get(pattern); !ok { 71 re, err = regexp.Compile(pattern) 72 if err != nil { 73 return nil, err 74 } 75 rc.set(pattern, re) 76 } 77 78 return re, nil 79 } 80 81 func (rc *regexpCache) get(key string) (re *regexp.Regexp, ok bool) { 82 rc.mu.RLock() 83 re, ok = rc.re[key] 84 rc.mu.RUnlock() 85 return 86 } 87 88 func (rc *regexpCache) set(key string, re *regexp.Regexp) { 89 rc.mu.Lock() 90 rc.re[key] = re 91 rc.mu.Unlock() 92 } 93 94 var reCache = regexpCache{re: make(map[string]*regexp.Regexp)} 95 96 // GetOrCompileRegexp retrieves a regexp object from the cache based upon the pattern. 97 // If the pattern is not found in the cache, the pattern is compiled and added to 98 // the cache. 99 func GetOrCompileRegexp(pattern string) (re *regexp.Regexp, err error) { 100 return reCache.getOrCompileRegexp(pattern) 101 } 102 103 // InSlice checks if a string is an element of a slice of strings 104 // and returns a boolean value. 105 func InSlice(arr []string, el string) bool { 106 for _, v := range arr { 107 if v == el { 108 return true 109 } 110 } 111 return false 112 } 113 114 // InSlicEqualFold checks if a string is an element of a slice of strings 115 // and returns a boolean value. 116 // It uses strings.EqualFold to compare. 117 func InSlicEqualFold(arr []string, el string) bool { 118 for _, v := range arr { 119 if strings.EqualFold(v, el) { 120 return true 121 } 122 } 123 return false 124 } 125 126 type Tuple struct { 127 First string 128 Second string 129 }