regexp.go (2916B)
1 // Copyright 2017 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 strings
15
16 import (
17 "regexp"
18 "sync"
19
20 "github.com/spf13/cast"
21 )
22
23 // FindRE returns a list of strings that match the regular expression. By default all matches
24 // will be included. The number of matches can be limited with an optional third parameter.
25 func (ns *Namespace) FindRE(expr string, content any, limit ...any) ([]string, error) {
26 re, err := reCache.Get(expr)
27 if err != nil {
28 return nil, err
29 }
30
31 conv, err := cast.ToStringE(content)
32 if err != nil {
33 return nil, err
34 }
35
36 if len(limit) == 0 {
37 return re.FindAllString(conv, -1), nil
38 }
39
40 lim, err := cast.ToIntE(limit[0])
41 if err != nil {
42 return nil, err
43 }
44
45 return re.FindAllString(conv, lim), nil
46 }
47
48 // ReplaceRE returns a copy of s, replacing all matches of the regular
49 // expression pattern with the replacement text repl. The number of replacements
50 // can be limited with an optional fourth parameter.
51 func (ns *Namespace) ReplaceRE(pattern, repl, s any, n ...any) (_ string, err error) {
52 sp, err := cast.ToStringE(pattern)
53 if err != nil {
54 return
55 }
56
57 sr, err := cast.ToStringE(repl)
58 if err != nil {
59 return
60 }
61
62 ss, err := cast.ToStringE(s)
63 if err != nil {
64 return
65 }
66
67 nn := -1
68 if len(n) > 0 {
69 nn, err = cast.ToIntE(n[0])
70 if err != nil {
71 return
72 }
73 }
74
75 re, err := reCache.Get(sp)
76 if err != nil {
77 return "", err
78 }
79
80 return re.ReplaceAllStringFunc(ss, func(str string) string {
81 if nn == 0 {
82 return str
83 }
84
85 nn -= 1
86 return re.ReplaceAllString(str, sr)
87 }), nil
88 }
89
90 // regexpCache represents a cache of regexp objects protected by a mutex.
91 type regexpCache struct {
92 mu sync.RWMutex
93 re map[string]*regexp.Regexp
94 }
95
96 // Get retrieves a regexp object from the cache based upon the pattern.
97 // If the pattern is not found in the cache, create one
98 func (rc *regexpCache) Get(pattern string) (re *regexp.Regexp, err error) {
99 var ok bool
100
101 if re, ok = rc.get(pattern); !ok {
102 re, err = regexp.Compile(pattern)
103 if err != nil {
104 return nil, err
105 }
106 rc.set(pattern, re)
107 }
108
109 return re, nil
110 }
111
112 func (rc *regexpCache) get(key string) (re *regexp.Regexp, ok bool) {
113 rc.mu.RLock()
114 re, ok = rc.re[key]
115 rc.mu.RUnlock()
116 return
117 }
118
119 func (rc *regexpCache) set(key string, re *regexp.Regexp) {
120 rc.mu.Lock()
121 rc.re[key] = re
122 rc.mu.Unlock()
123 }
124
125 var reCache = regexpCache{re: make(map[string]*regexp.Regexp)}