diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2019-08-12 16:43:37 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2019-08-13 11:44:20 +0200 |
commit | b64617fe4f90da030bcf4a9c5a4913393ce96b14 (patch) | |
tree | 07240dbf51bb4afef9ea063f2310c1617be6bb0a /resources | |
parent | 17ca8f0c4c636752fb9da2ad551679275dc03dd3 (diff) |
Add resources.Match and resources.GetMatch
Fix #6190
Diffstat (limited to 'resources')
-rw-r--r-- | resources/internal/glob.go | 48 | ||||
-rw-r--r-- | resources/resource/resources.go | 6 | ||||
-rw-r--r-- | resources/resource_cache.go | 28 | ||||
-rw-r--r-- | resources/resource_factories/create/create.go | 68 | ||||
-rw-r--r-- | resources/resource_metadata.go | 4 |
5 files changed, 92 insertions, 62 deletions
diff --git a/resources/internal/glob.go b/resources/internal/glob.go deleted file mode 100644 index a87a23f13..000000000 --- a/resources/internal/glob.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019 The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package internal - -import ( - "strings" - "sync" - - "github.com/gobwas/glob" -) - -var ( - globCache = make(map[string]glob.Glob) - globMu sync.RWMutex -) - -func GetGlob(pattern string) (glob.Glob, error) { - var g glob.Glob - - globMu.RLock() - g, found := globCache[pattern] - globMu.RUnlock() - if !found { - var err error - g, err = glob.Compile(strings.ToLower(pattern), '/') - if err != nil { - return nil, err - } - - globMu.Lock() - globCache[pattern] = g - globMu.Unlock() - } - - return g, nil - -} diff --git a/resources/resource/resources.go b/resources/resource/resources.go index 5c661c24e..ac5dd0b2b 100644 --- a/resources/resource/resources.go +++ b/resources/resource/resources.go @@ -17,7 +17,7 @@ import ( "fmt" "strings" - "github.com/gohugoio/hugo/resources/internal" + "github.com/gohugoio/hugo/hugofs/glob" ) // Resources represents a slice of resources, which can be a mix of different types. @@ -44,7 +44,7 @@ func (r Resources) ByType(tp string) Resources { // 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 := internal.GetGlob(pattern) + g, err := glob.GetGlob(pattern) if err != nil { return nil } @@ -68,7 +68,7 @@ func (r Resources) GetMatch(pattern string) Resource { // 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 := internal.GetGlob(pattern) + g, err := glob.GetGlob(pattern) if err != nil { return nil } diff --git a/resources/resource_cache.go b/resources/resource_cache.go index 8ff63beb0..656d4f826 100644 --- a/resources/resource_cache.go +++ b/resources/resource_cache.go @@ -37,7 +37,9 @@ type ResourceCache struct { rs *Spec sync.RWMutex - cache map[string]resource.Resource + + // Either resource.Resource or resource.Resources. + cache map[string]interface{} fileCache *filecache.Cache @@ -61,7 +63,7 @@ func newResourceCache(rs *Spec) *ResourceCache { return &ResourceCache{ rs: rs, fileCache: rs.FileCaches.AssetsCache(), - cache: make(map[string]resource.Resource), + cache: make(map[string]interface{}), nlocker: locker.NewLocker(), } } @@ -70,7 +72,7 @@ func (c *ResourceCache) clear() { c.Lock() defer c.Unlock() - c.cache = make(map[string]resource.Resource) + c.cache = make(map[string]interface{}) c.nlocker = locker.NewLocker() } @@ -84,7 +86,7 @@ func (c *ResourceCache) cleanKey(key string) string { return strings.TrimPrefix(path.Clean(key), "/") } -func (c *ResourceCache) get(key string) (resource.Resource, bool) { +func (c *ResourceCache) get(key string) (interface{}, bool) { c.RLock() defer c.RUnlock() r, found := c.cache[key] @@ -92,6 +94,22 @@ func (c *ResourceCache) get(key string) (resource.Resource, bool) { } func (c *ResourceCache) GetOrCreate(partition, key string, f func() (resource.Resource, error)) (resource.Resource, error) { + r, err := c.getOrCreate(partition, key, func() (interface{}, error) { return f() }) + if r == nil || err != nil { + return nil, err + } + return r.(resource.Resource), nil +} + +func (c *ResourceCache) GetOrCreateResources(partition, key string, f func() (resource.Resources, error)) (resource.Resources, error) { + r, err := c.getOrCreate(partition, key, func() (interface{}, error) { return f() }) + if r == nil || err != nil { + return nil, err + } + return r.(resource.Resources), nil +} + +func (c *ResourceCache) getOrCreate(partition, key string, f func() (interface{}, error)) (interface{}, error) { key = c.cleanKey(path.Join(partition, key)) // First check in-memory cache. r, found := c.get(key) @@ -174,7 +192,7 @@ func (c *ResourceCache) writeMeta(key string, meta transformedResourceMetadata) } -func (c *ResourceCache) set(key string, r resource.Resource) { +func (c *ResourceCache) set(key string, r interface{}) { c.Lock() defer c.Unlock() c.cache[key] = r diff --git a/resources/resource_factories/create/create.go b/resources/resource_factories/create/create.go index 36a29e733..e42843c75 100644 --- a/resources/resource_factories/create/create.go +++ b/resources/resource_factories/create/create.go @@ -16,9 +16,12 @@ package create import ( + "path" "path/filepath" - "github.com/spf13/afero" + "github.com/gohugoio/hugo/hugofs/glob" + + "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/common/hugio" "github.com/gohugoio/hugo/resources" @@ -36,18 +39,75 @@ func New(rs *resources.Spec) *Client { return &Client{rs: rs} } -// Get creates a new Resource by opening the given filename in the given filesystem. -func (c *Client) Get(fs afero.Fs, filename string) (resource.Resource, error) { +// Get creates a new Resource by opening the given filename in the assets filesystem. +func (c *Client) Get(filename string) (resource.Resource, error) { filename = filepath.Clean(filename) return c.rs.ResourceCache.GetOrCreate(resources.ResourceKeyPartition(filename), filename, func() (resource.Resource, error) { return c.rs.New(resources.ResourceSourceDescriptor{ - Fs: fs, + Fs: c.rs.BaseFs.Assets.Fs, LazyPublish: true, SourceFilename: filename}) }) } +// Match gets the resources matching the given pattern from the assets filesystem. +func (c *Client) Match(pattern string) (resource.Resources, error) { + return c.match(pattern, false) +} + +// GetMatch gets first resource matching the given pattern from the assets filesystem. +func (c *Client) GetMatch(pattern string) (resource.Resource, error) { + res, err := c.match(pattern, true) + if err != nil || len(res) == 0 { + return nil, err + } + return res[0], err +} + +func (c *Client) match(pattern string, firstOnly bool) (resource.Resources, error) { + var partition string + if firstOnly { + partition = "__get-match" + } else { + partition = "__match" + } + + // TODO(bep) match will be improved as part of https://github.com/gohugoio/hugo/issues/6199 + partition = path.Join(resources.CACHE_OTHER, partition) + key := glob.NormalizePath(pattern) + + return c.rs.ResourceCache.GetOrCreateResources(partition, key, func() (resource.Resources, error) { + var res resource.Resources + + handle := func(info hugofs.FileMetaInfo) (bool, error) { + meta := info.Meta() + r, err := c.rs.New(resources.ResourceSourceDescriptor{ + LazyPublish: true, + OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) { + return meta.Open() + }, + RelTargetFilename: meta.Path()}) + + if err != nil { + return true, err + } + + res = append(res, r) + + return firstOnly, nil + + } + + if err := hugofs.Glob(c.rs.BaseFs.Assets.Fs, pattern, handle); err != nil { + return nil, err + } + + return res, nil + + }) +} + // FromString creates a new Resource from a string with the given relative target path. func (c *Client) FromString(targetPath, content string) (resource.Resource, error) { return c.rs.ResourceCache.GetOrCreate(resources.CACHE_OTHER, targetPath, func() (resource.Resource, error) { diff --git a/resources/resource_metadata.go b/resources/resource_metadata.go index e019133d7..adb9d6867 100644 --- a/resources/resource_metadata.go +++ b/resources/resource_metadata.go @@ -17,7 +17,7 @@ import ( "fmt" "strconv" - "github.com/gohugoio/hugo/resources/internal" + "github.com/gohugoio/hugo/hugofs/glob" "github.com/gohugoio/hugo/resources/resource" "github.com/pkg/errors" @@ -70,7 +70,7 @@ func AssignMetadata(metadata []map[string]interface{}, resources ...resource.Res srcKey := strings.ToLower(cast.ToString(src)) - glob, err := internal.GetGlob(srcKey) + glob, err := glob.GetGlob(srcKey) if err != nil { return errors.Wrap(err, "failed to match resource with metadata") } |