URI: 
       Fix union, complement, symdiff, and intersect for transient resources - hugo - [fork] hugo port for 9front
  HTML git clone git@git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
   DIR commit 4a5e94087ba2fe1405ad2edbcbeeccac2a6147a6
   DIR parent 48a7aee961de83ce5ee1b9ada06567878665a795
  HTML Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Sun, 22 Dec 2024 17:59:03 +0100
       
       Fix union, complement, symdiff, and intersect for transient resources
       
       Fixes #13181
       
       Diffstat:
         M resources/resource.go               |      32 ++++++++++++++++++++-----------
         M resources/resource/resourcetypes.go |      10 +++++++++-
         M resources/resource_spec.go          |       1 +
         M resources/transform.go              |       6 ++++++
         M tpl/collections/collections_integr… |      29 +++++++++++++++++++++++++++++
         M tpl/collections/reflect_helpers.go  |      12 +++++++++---
       
       6 files changed, 75 insertions(+), 15 deletions(-)
       ---
   DIR diff --git a/resources/resource.go b/resources/resource.go
       @@ -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
        }
        
   DIR diff --git a/resources/resource/resourcetypes.go b/resources/resource/resourcetypes.go
       @@ -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
   DIR diff --git a/resources/resource_spec.go b/resources/resource_spec.go
       @@ -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,
   DIR diff --git a/resources/transform.go b/resources/transform.go
       @@ -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()
   DIR diff --git a/tpl/collections/collections_integration_test.go b/tpl/collections/collections_integration_test.go
       @@ -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$")
       +}
   DIR diff --git a/tpl/collections/reflect_helpers.go b/tpl/collections/reflect_helpers.go
       @@ -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() {