summaryrefslogtreecommitdiffstats
path: root/resource/resource.go
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2018-01-20 18:07:41 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2018-01-22 10:28:12 +0100
commit9421380168f66620cb73203e1267814b3086d805 (patch)
tree0946c9982ebfd61bb74268e69302e2364171d6f2 /resource/resource.go
parent5d03086981b4a7d4bc450269a6a2e0fd22dbeed7 (diff)
resource: Add Match and GetMatch
These methods takes a glob pattern as argument: * by default matching from the bundle root * matching is case insensitive and the separator is Unix style slashes: "/" * the bundle root does (by default) not start with a leading slash * if you renames the `Name` for the rsource in front matter (`src=...`), then that is the value used in `Match`. * double asterisk matches beyond directory borders, so "**.jpg" will match any JPEG image in the bundle See https://github.com/gobwas/glob This commit also deprecates `ByPrefix` and `GetByPrefix`. This should also be more effective, given a fair amount of reuse of the glob patterns: ```bash BenchmarkResourcesByPrefix-4 300000 4284 ns/op 1130 B/op 7 allocs/op BenchmarkResourcesMatch-4 300000 5220 ns/op 505 B/op 3 allocs/op ``` Fixes #4301
Diffstat (limited to 'resource/resource.go')
-rw-r--r--resource/resource.go91
1 files changed, 88 insertions, 3 deletions
diff --git a/resource/resource.go b/resource/resource.go
index b3a92273d..a2128bdc7 100644
--- a/resource/resource.go
+++ b/resource/resource.go
@@ -21,13 +21,14 @@ import (
"path/filepath"
"strconv"
"strings"
+ "sync"
"github.com/spf13/cast"
+ "github.com/gobwas/glob"
+ "github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/source"
-
- "github.com/gohugoio/hugo/helpers"
)
var (
@@ -101,10 +102,21 @@ func (r Resources) ByType(tp string) Resources {
return filtered
}
+const prefixDeprecatedMsg = `We have added the more flexible Resources.GetMatch (find one) and Resources.Match (many) to replace the "prefix" methods.
+
+These matches by a given globbing pattern, e.g. "*.jpg".
+
+Some examples:
+
+* To find all resources by its prefix in the root dir of the bundle: .Match image*
+* To find one resource by its prefix in the root dir of the bundle: .GetMatch image*
+* To find all JPEG images anywhere in the bundle: .Match **.jpg`
+
// GetBySuffix gets the first resource matching the given filename prefix, e.g
// "logo" will match logo.png. It returns nil of none found.
// In potential ambiguous situations, combine it with ByType.
func (r Resources) GetByPrefix(prefix string) Resource {
+ helpers.Deprecated("Resources", "GetByPrefix", prefixDeprecatedMsg, false)
prefix = strings.ToLower(prefix)
for _, resource := range r {
if matchesPrefix(resource, prefix) {
@@ -117,6 +129,7 @@ func (r Resources) GetByPrefix(prefix string) Resource {
// ByPrefix gets all resources matching the given base filename prefix, e.g
// "logo" will match logo.png.
func (r Resources) ByPrefix(prefix string) Resources {
+ helpers.Deprecated("Resources", "ByPrefix", prefixDeprecatedMsg, false)
var matches Resources
prefix = strings.ToLower(prefix)
for _, resource := range r {
@@ -127,10 +140,80 @@ func (r Resources) ByPrefix(prefix string) Resources {
return matches
}
+// GetMatch finds the first Resource matching the given pattern, or nil if none found.
+// See Match for a more complete explanation about the rules used.
+func (r Resources) GetMatch(pattern string) Resource {
+ g, err := getGlob(pattern)
+ if err != nil {
+ return nil
+ }
+
+ for _, resource := range r {
+ if g.Match(strings.ToLower(resource.Name())) {
+ return resource
+ }
+ }
+
+ return nil
+}
+
+// Match gets all resources matching the given base filename prefix, e.g
+// "*.png" will match all png files. The "*" does not match path delimiters (/),
+// so if you organize your resources in sub-folders, you need to be explicit about it, e.g.:
+// "images/*.png". To match any PNG image anywhere in the bundle you can do "**.png", and
+// to match all PNG images below the images folder, use "images/**.jpg".
+// The matching is case insensitive.
+// Match matches by using the value of Resource.Name, which, by default, is a filename with
+// path relative to the bundle root with Unix style slashes (/) and no leading slash, e.g. "images/logo.png".
+// See https://github.com/gobwas/glob for the full rules set.
+func (r Resources) Match(pattern string) Resources {
+ g, err := getGlob(pattern)
+ if err != nil {
+ return nil
+ }
+
+ var matches Resources
+ for _, resource := range r {
+ if g.Match(strings.ToLower(resource.Name())) {
+ matches = append(matches, resource)
+ }
+ }
+ return matches
+}
+
func matchesPrefix(r Resource, prefix string) bool {
return strings.HasPrefix(strings.ToLower(r.Name()), prefix)
}
+var (
+ globCache = make(map[string]glob.Glob)
+ globMu sync.RWMutex
+)
+
+func getGlob(pattern string) (glob.Glob, error) {
+ pattern = strings.ToLower(pattern)
+
+ var g glob.Glob
+
+ globMu.RLock()
+ g, found := globCache[pattern]
+ globMu.RUnlock()
+ if !found {
+ var err error
+ g, err = glob.Compile(pattern, '/')
+ if err != nil {
+ return nil, err
+ }
+
+ globMu.Lock()
+ globCache[pattern] = g
+ globMu.Unlock()
+ }
+
+ return g, nil
+
+}
+
type Spec struct {
*helpers.PathSpec
mimeTypes media.Types
@@ -390,11 +473,13 @@ func AssignMetadata(metadata []map[string]interface{}, resources ...Resource) er
srcKey := strings.ToLower(cast.ToString(src))
- match, err := path.Match(srcKey, resourceSrcKey)
+ glob, err := getGlob(srcKey)
if err != nil {
return fmt.Errorf("failed to match resource with metadata: %s", err)
}
+ match := glob.Match(resourceSrcKey)
+
if match {
if !nameSet {
name, found := meta["name"]