]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Fix union, complement, symdiff, and intersect for transient resources
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sun, 22 Dec 2024 16:59:03 +0000 (17:59 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sun, 22 Dec 2024 18:25:03 +0000 (19:25 +0100)
Fixes #13181

resources/resource.go
resources/resource/resourcetypes.go
resources/resource_spec.go
resources/transform.go
tpl/collections/collections_integration_test.go
tpl/collections/reflect_helpers.go

index 4b81a478a427a233d143ecfbcc96d832d3a75013..7ab10b0ae34db7413f22b8f6ced0c9dc1fd7cc45 100644 (file)
@@ -47,6 +47,7 @@ var (
        _ resource.Cloner                    = (*genericResource)(nil)
        _ resource.ResourcesLanguageMerger   = (*resource.Resources)(nil)
        _ resource.Identifier                = (*genericResource)(nil)
+       _ resource.TransientIdentifier       = (*genericResource)(nil)
        _ targetPathProvider                 = (*genericResource)(nil)
        _ sourcePathProvider                 = (*genericResource)(nil)
        _ identity.IdentityGroupProvider     = (*genericResource)(nil)
@@ -359,6 +360,9 @@ func GetTestInfoForResource(r resource.Resource) GenericResourceTestInfo {
 type genericResource struct {
        publishInit *sync.Once
 
+       key     string
+       keyInit *sync.Once
+
        sd    ResourceSourceDescriptor
        paths internal.ResourcePaths
 
@@ -444,19 +448,24 @@ func (l *genericResource) Data() any {
 }
 
 func (l *genericResource) Key() string {
-       basePath := l.spec.Cfg.BaseURL().BasePathNoTrailingSlash
-       var key string
-       if basePath == "" {
-               key = l.RelPermalink()
-       } else {
-               key = strings.TrimPrefix(l.RelPermalink(), basePath)
-       }
+       l.keyInit.Do(func() {
+               basePath := l.spec.Cfg.BaseURL().BasePathNoTrailingSlash
+               if basePath == "" {
+                       l.key = l.RelPermalink()
+               } else {
+                       l.key = strings.TrimPrefix(l.RelPermalink(), basePath)
+               }
 
-       if l.spec.Cfg.IsMultihost() {
-               key = l.spec.Lang() + key
-       }
+               if l.spec.Cfg.IsMultihost() {
+                       l.key = l.spec.Lang() + l.key
+               }
+       })
+
+       return l.key
+}
 
-       return key
+func (l *genericResource) TransientKey() string {
+       return l.Key()
 }
 
 func (l *genericResource) targetPath() string {
@@ -623,6 +632,7 @@ func (rc *genericResource) cloneWithUpdates(u *transformationUpdate) (baseResour
 
 func (l genericResource) clone() *genericResource {
        l.publishInit = &sync.Once{}
+       l.keyInit = &sync.Once{}
        return &l
 }
 
index 0fb87f371379b259f3390bf508421c3a0f63ba96..b33750e8033b294a508fb4718e7970585c6a2180 100644 (file)
@@ -170,11 +170,19 @@ type ResourcesLanguageMerger interface {
 
 // Identifier identifies a resource.
 type Identifier interface {
-       // Key is is mostly for internal use and should be considered opaque.
+       // Key is mostly for internal use and should be considered opaque.
        // This value may change between Hugo versions.
        Key() string
 }
 
+// TransientIdentifier identifies a transient resource.
+type TransientIdentifier interface {
+       // TransientKey is mostly for internal use and should be considered opaque.
+       // This value is implemented by transient resources where pointers may be short lived and
+       // not suitable for use as a map keys.
+       TransientKey() string
+}
+
 // WeightProvider provides a weight.
 type WeightProvider interface {
        Weight() int
index d50edeb73ce38154d25cbd7a7270e2f72b8cd984..912a0d786f0acd35097af3546ece6d940adc5a9f 100644 (file)
@@ -187,6 +187,7 @@ func (r *Spec) NewResource(rd ResourceSourceDescriptor) (resource.Resource, erro
                Staler:      &AtomicStaler{},
                h:           &resourceHash{},
                publishInit: &sync.Once{},
+               keyInit:     &sync.Once{},
                paths:       rp,
                spec:        r,
                sd:          rd,
index 4214067bddc3343372d39fd69505ba84dab1db34..c5d24066937fd01e891ae294c937f6fb3ead78cc 100644 (file)
@@ -52,8 +52,10 @@ var (
        _ identity.IdentityGroupProvider     = (*resourceAdapterInner)(nil)
        _ resource.Source                    = (*resourceAdapter)(nil)
        _ resource.Identifier                = (*resourceAdapter)(nil)
+       _ resource.TransientIdentifier       = (*resourceAdapter)(nil)
        _ targetPathProvider                 = (*resourceAdapter)(nil)
        _ sourcePathProvider                 = (*resourceAdapter)(nil)
+       _ resource.Identifier                = (*resourceAdapter)(nil)
        _ resource.ResourceNameTitleProvider = (*resourceAdapter)(nil)
        _ resource.WithResourceMetaProvider  = (*resourceAdapter)(nil)
        _ identity.DependencyManagerProvider = (*resourceAdapter)(nil)
@@ -279,6 +281,10 @@ func (r *resourceAdapter) Key() string {
        return r.target.(resource.Identifier).Key()
 }
 
+func (r *resourceAdapter) TransientKey() string {
+       return r.Key()
+}
+
 func (r *resourceAdapter) targetPath() string {
        r.init(false, false)
        return r.target.(targetPathProvider).targetPath()
index e39493b529bf5c21f569d714334fa9b3125d37af..2aabee03e5c689c62f560d73cac2bfb1336b701a 100644 (file)
@@ -249,3 +249,32 @@ tags: ['tag-b']
                "2: Intersect: 1|\n2: Union: 3|\n2: SymDiff: 2|\n2: Uniq: 3|",
        )
 }
+
+// Issue #13181
+func TestUnionResourcesMatch(t *testing.T) {
+       t.Parallel()
+
+       files := `
+-- config.toml --
+disableKinds = ['rss','sitemap', 'taxonomy', 'term', 'page']
+-- layouts/index.html --
+{{ $a := resources.Match "*a*" }}
+{{ $b := resources.Match "*b*" }}
+{{ $union := $a | union $b }}
+{{ range $i, $e := $union }}
+{{ $i }}: {{ .Name }}
+{{ end }}$
+-- assets/a1.html --
+<div>file1</div>
+-- assets/a2.html --
+<div>file2</div>
+-- assets/a3_b1.html --
+<div>file3</div>
+-- assets/b2.html --
+<div>file4</div>
+`
+
+       b := hugolib.Test(t, files)
+
+       b.AssertFileContentExact("public/index.html", "0: /a3_b1.html\n\n1: /b2.html\n\n2: /a1.html\n\n3: /a2.html\n$")
+}
index 4b222be15cacc772feb4009f9b1a7483889b665f..6b986cbc4ce7749df562637d8be7ca089bccef4c 100644 (file)
@@ -20,11 +20,12 @@ import (
 
        "github.com/gohugoio/hugo/common/hashing"
        "github.com/gohugoio/hugo/common/types"
+       "github.com/gohugoio/hugo/resources/resource"
 )
 
 var (
        zero      reflect.Value
-       errorType = reflect.TypeOf((*error)(nil)).Elem()
+       errorType = reflect.TypeFor[error]()
 )
 
 func numberToFloat(v reflect.Value) (float64, error) {
@@ -56,7 +57,13 @@ func normalize(v reflect.Value) any {
                        return f
                }
        }
-       return types.Unwrapv(v.Interface())
+
+       vv := types.Unwrapv(v.Interface())
+       if ip, ok := vv.(resource.TransientIdentifier); ok {
+               return ip.TransientKey()
+       }
+
+       return vv
 }
 
 // collects identities from the slices in seqs into a set. Numeric values are normalized,
@@ -151,7 +158,6 @@ func convertNumber(v reflect.Value, to reflect.Kind) (reflect.Value, error) {
                case reflect.Uint64:
                        n = reflect.ValueOf(uint64(i))
                }
-
        }
 
        if !n.IsValid() {