diff --git a/hugolib/hugo_sites_build_test.go b/hugolib/hugo_sites_build_test.go index fdfc33c5a15..a90f2799d36 100644 --- a/hugolib/hugo_sites_build_test.go +++ b/hugolib/hugo_sites_build_test.go @@ -396,7 +396,9 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) { bundleFr := frSite.getPage(page.KindPage, "bundles/b1/index.md") c.Assert(bundleFr, qt.Not(qt.IsNil)) c.Assert(len(bundleFr.Resources()), qt.Equals, 1) - logoFr := bundleFr.Resources().GetMatch("logo*") + logoFr, _ := bundleFr.Resources().GetMatch("logo*") + logoFrGet, _ := bundleFr.Resources().Get("logo.png") + c.Assert(logoFrGet, qt.Equals, logoFr) c.Assert(logoFr, qt.Not(qt.IsNil)) b.AssertFileContent("public/fr/bundles/b1/index.html", "Resources: image/png: /blog/fr/bundles/b1/logo.png") b.AssertFileContent("public/fr/bundles/b1/logo.png", "PNG Data") @@ -405,7 +407,7 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) { c.Assert(bundleEn, qt.Not(qt.IsNil)) b.AssertFileContent("public/en/bundles/b1/index.html", "RelPermalink: /blog/en/bundles/b1/|") c.Assert(len(bundleEn.Resources()), qt.Equals, 1) - logoEn := bundleEn.Resources().GetMatch("logo*") + logoEn, _ := bundleEn.Resources().GetMatch("logo*") c.Assert(logoEn, qt.Not(qt.IsNil)) b.AssertFileContent("public/en/bundles/b1/index.html", "Resources: image/png: /blog/en/bundles/b1/logo.png") b.AssertFileContent("public/en/bundles/b1/logo.png", "PNG Data") diff --git a/hugolib/pagebundler_test.go b/hugolib/pagebundler_test.go index 1694b02ee8a..bbeb508b4eb 100644 --- a/hugolib/pagebundler_test.go +++ b/hugolib/pagebundler_test.go @@ -23,6 +23,8 @@ import ( "strings" "testing" + "github.com/gohugoio/hugo/resources/resource" + "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/hugofs/files" @@ -184,9 +186,17 @@ func TestPageBundlerSiteRegular(t *testing.T) { c.Assert(firstPage.Parent(), qt.Equals, leafBundle1) c.Assert(secondPage.Parent(), qt.Equals, leafBundle1) - c.Assert(pageResources.GetMatch("1*"), qt.Equals, firstPage) - c.Assert(pageResources.GetMatch("2*"), qt.Equals, secondPage) - c.Assert(pageResources.GetMatch("doesnotexist*"), qt.IsNil) + mustGetMatch := func(r resource.Resources, pattern string) resource.Resource { + res, err := r.GetMatch(pattern) + if err != nil { + panic(err) + } + return res + } + + c.Assert(mustGetMatch(pageResources, "1*"), qt.Equals, firstPage) + c.Assert(mustGetMatch(pageResources, "2*"), qt.Equals, secondPage) + c.Assert(mustGetMatch(pageResources, "doesnotexist*"), qt.IsNil) imageResources := leafBundle1.Resources().ByType("image") c.Assert(len(imageResources), qt.Equals, 3) @@ -327,9 +337,9 @@ func TestPageBundlerSiteMultilingual(t *testing.T) { // See https://github.com/gohugoio/hugo/issues/4295 // Every resource should have its Name prefixed with its base folder. - cBundleResources := bundleWithSubPath.Resources().Match("c/**") + cBundleResources, _ := bundleWithSubPath.Resources().Match("c/**") c.Assert(len(cBundleResources), qt.Equals, 4) - bundlePage := bundleWithSubPath.Resources().GetMatch("c/page*") + bundlePage, _ := bundleWithSubPath.Resources().GetMatch("c/page*") c.Assert(bundlePage, qt.Not(qt.IsNil)) bcBundleNN, _ := nnSite.getPageNew(nil, "bc") @@ -553,8 +563,9 @@ HEADLESS {{< myShort >}} headlessResources := headless.Resources() c.Assert(len(headlessResources), qt.Equals, 3) - c.Assert(len(headlessResources.Match("l*")), qt.Equals, 2) - pageResource := headlessResources.GetMatch("p*") + res, _ := headlessResources.Match("l*") + c.Assert(len(res), qt.Equals, 2) + pageResource, _ := headlessResources.GetMatch("p*") c.Assert(pageResource, qt.Not(qt.IsNil)) p := pageResource.(page.Page) c.Assert(content(p), qt.Contains, "SHORTCODE") diff --git a/resources/resource/resources.go b/resources/resource/resources.go index ac5dd0b2b03..e87c742b2f6 100644 --- a/resources/resource/resources.go +++ b/resources/resource/resources.go @@ -18,12 +18,16 @@ import ( "strings" "github.com/gohugoio/hugo/hugofs/glob" + "github.com/spf13/cast" ) +var _ ResourceFinder = (*Resources)(nil) + // Resources represents a slice of resources, which can be a mix of different types. // I.e. both pages and images etc. type Resources []Resource +// var _ resource.ResourceFinder = (*Namespace)(nil) // ResourcesConverter converts a given slice of Resource objects to Resources. type ResourcesConverter interface { ToResources() Resources @@ -41,21 +45,42 @@ func (r Resources) ByType(tp string) Resources { return filtered } +// Get locates the name given in Resources. +// The search is case insensitive. +func (r Resources) Get(name interface{}) (Resource, error) { + namestr, err := cast.ToStringE(name) + if err != nil { + return nil, err + } + namestr = strings.ToLower(namestr) + for _, resource := range r { + if strings.EqualFold(namestr, resource.Name()) { + return resource, nil + } + } + return nil, nil +} + // 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 := glob.GetGlob(pattern) +func (r Resources) GetMatch(pattern interface{}) (Resource, error) { + patternstr, err := cast.ToStringE(pattern) if err != nil { - return nil + return nil, err + } + + g, err := glob.GetGlob(patternstr) + if err != nil { + return nil, err } for _, resource := range r { if g.Match(strings.ToLower(resource.Name())) { - return resource + return resource, nil } } - return nil + return nil, nil } // Match gets all resources matching the given base filename prefix, e.g @@ -67,10 +92,15 @@ func (r Resources) GetMatch(pattern string) Resource { // 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 := glob.GetGlob(pattern) +func (r Resources) Match(pattern interface{}) (Resources, error) { + patternstr, err := cast.ToStringE(pattern) + if err != nil { + return nil, err + } + + g, err := glob.GetGlob(patternstr) if err != nil { - return nil + return nil, err } var matches Resources @@ -79,7 +109,7 @@ func (r Resources) Match(pattern string) Resources { matches = append(matches, resource) } } - return matches + return matches, nil } type translatedResource interface { @@ -121,3 +151,34 @@ func (r Resources) MergeByLanguageInterface(in interface{}) (interface{}, error) type Source interface { Publish() error } + +// ResourceFinder provides method to find Resources. +type ResourceFinder interface { + + // Get locates the filename given in Hugo's assets filesystem + // and creates a Resource object that can be used for further transformations. + Get(filename interface{}) (Resource, error) + + // 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. + GetMatch(pattern interface{}) (Resource, error) + + // Match gets all resources matching the given base path 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 a relative pathwith Unix style slashes (/) and no + // leading slash, e.g. "images/logo.png". + // + // See https://github.com/gobwas/glob for the full rules set. + // + // It looks for files in the assets file system. + // + // See Match for a more complete explanation about the rules used. + Match(pattern interface{}) (Resources, error) +} diff --git a/tpl/resources/resources.go b/tpl/resources/resources.go index 850def00e27..d8affb4abe3 100644 --- a/tpl/resources/resources.go +++ b/tpl/resources/resources.go @@ -73,6 +73,8 @@ func New(deps *deps.Deps) (*Namespace, error) { }, nil } +var _ resource.ResourceFinder = (*Namespace)(nil) + // Namespace provides template functions for the "resources" namespace. type Namespace struct { deps *deps.Deps