mirror of
https://github.com/gohugoio/hugo.git
synced 2025-04-26 13:40:38 +03:00
parent
77a8e347bc
commit
7f0f50b133
10 changed files with 318 additions and 48 deletions
|
@ -123,16 +123,24 @@ func HashUint64(vs ...any) uint64 {
|
||||||
o = elements
|
o = elements
|
||||||
}
|
}
|
||||||
|
|
||||||
hashOpts := getHashOpts()
|
hash, err := Hash(o)
|
||||||
defer putHashOpts(hashOpts)
|
|
||||||
|
|
||||||
hash, err := hashstructure.Hash(o, hashOpts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hash returns a hash from vs.
|
||||||
|
func Hash(vs ...any) (uint64, error) {
|
||||||
|
hashOpts := getHashOpts()
|
||||||
|
defer putHashOpts(hashOpts)
|
||||||
|
var v any = vs
|
||||||
|
if len(vs) == 1 {
|
||||||
|
v = vs[0]
|
||||||
|
}
|
||||||
|
return hashstructure.Hash(v, hashOpts)
|
||||||
|
}
|
||||||
|
|
||||||
type keyer interface {
|
type keyer interface {
|
||||||
Key() string
|
Key() string
|
||||||
}
|
}
|
||||||
|
|
133
common/maps/ordered.go
Normal file
133
common/maps/ordered.go
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
// Copyright 2024 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 maps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gohugoio/hugo/common/hashing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ordered is a map that can be iterated in the order of insertion.
|
||||||
|
// Note that insertion order is not affected if a key is re-inserted into the map.
|
||||||
|
// In a nil map, all operations are no-ops.
|
||||||
|
// This is not thread safe.
|
||||||
|
type Ordered[K comparable, T any] struct {
|
||||||
|
// The keys in the order they were added.
|
||||||
|
keys []K
|
||||||
|
// The values.
|
||||||
|
values map[K]T
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOrdered creates a new Ordered map.
|
||||||
|
func NewOrdered[K comparable, T any]() *Ordered[K, T] {
|
||||||
|
return &Ordered[K, T]{values: make(map[K]T)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the value for the given key.
|
||||||
|
// Note that insertion order is not affected if a key is re-inserted into the map.
|
||||||
|
func (m *Ordered[K, T]) Set(key K, value T) {
|
||||||
|
if m == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Check if key already exists.
|
||||||
|
if _, found := m.values[key]; !found {
|
||||||
|
m.keys = append(m.keys, key)
|
||||||
|
}
|
||||||
|
m.values[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets the value for the given key.
|
||||||
|
func (m *Ordered[K, T]) Get(key K) (T, bool) {
|
||||||
|
if m == nil {
|
||||||
|
var v T
|
||||||
|
return v, false
|
||||||
|
}
|
||||||
|
value, found := m.values[key]
|
||||||
|
return value, found
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the value for the given key.
|
||||||
|
func (m *Ordered[K, T]) Delete(key K) {
|
||||||
|
if m == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(m.values, key)
|
||||||
|
for i, k := range m.keys {
|
||||||
|
if k == key {
|
||||||
|
m.keys = append(m.keys[:i], m.keys[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone creates a shallow copy of the map.
|
||||||
|
func (m *Ordered[K, T]) Clone() *Ordered[K, T] {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
clone := NewOrdered[K, T]()
|
||||||
|
for _, k := range m.keys {
|
||||||
|
clone.Set(k, m.values[k])
|
||||||
|
}
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns the keys in the order they were added.
|
||||||
|
func (m *Ordered[K, T]) Keys() []K {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m.keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values returns the values in the order they were added.
|
||||||
|
func (m *Ordered[K, T]) Values() []T {
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var values []T
|
||||||
|
for _, k := range m.keys {
|
||||||
|
values = append(values, m.values[k])
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of items in the map.
|
||||||
|
func (m *Ordered[K, T]) Len() int {
|
||||||
|
if m == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return len(m.keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range calls f sequentially for each key and value present in the map.
|
||||||
|
// If f returns false, range stops the iteration.
|
||||||
|
// TODO(bep) replace with iter.Seq2 when we bump go Go 1.24.
|
||||||
|
func (m *Ordered[K, T]) Range(f func(key K, value T) bool) {
|
||||||
|
if m == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, k := range m.keys {
|
||||||
|
if !f(k, m.values[k]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash calculates a hash from the values.
|
||||||
|
func (m *Ordered[K, T]) Hash() (uint64, error) {
|
||||||
|
if m == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return hashing.Hash(m.values)
|
||||||
|
}
|
99
common/maps/ordered_test.go
Normal file
99
common/maps/ordered_test.go
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
// Copyright 2024 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 maps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
qt "github.com/frankban/quicktest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOrdered(t *testing.T) {
|
||||||
|
c := qt.New(t)
|
||||||
|
|
||||||
|
m := NewOrdered[string, int]()
|
||||||
|
m.Set("a", 1)
|
||||||
|
m.Set("b", 2)
|
||||||
|
m.Set("c", 3)
|
||||||
|
|
||||||
|
c.Assert(m.Keys(), qt.DeepEquals, []string{"a", "b", "c"})
|
||||||
|
c.Assert(m.Values(), qt.DeepEquals, []int{1, 2, 3})
|
||||||
|
|
||||||
|
v, found := m.Get("b")
|
||||||
|
c.Assert(found, qt.Equals, true)
|
||||||
|
c.Assert(v, qt.Equals, 2)
|
||||||
|
|
||||||
|
m.Set("b", 22)
|
||||||
|
c.Assert(m.Keys(), qt.DeepEquals, []string{"a", "b", "c"})
|
||||||
|
c.Assert(m.Values(), qt.DeepEquals, []int{1, 22, 3})
|
||||||
|
|
||||||
|
m.Delete("b")
|
||||||
|
|
||||||
|
c.Assert(m.Keys(), qt.DeepEquals, []string{"a", "c"})
|
||||||
|
c.Assert(m.Values(), qt.DeepEquals, []int{1, 3})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedHash(t *testing.T) {
|
||||||
|
c := qt.New(t)
|
||||||
|
|
||||||
|
m := NewOrdered[string, int]()
|
||||||
|
m.Set("a", 1)
|
||||||
|
m.Set("b", 2)
|
||||||
|
m.Set("c", 3)
|
||||||
|
|
||||||
|
h1, err := m.Hash()
|
||||||
|
c.Assert(err, qt.IsNil)
|
||||||
|
|
||||||
|
m.Set("d", 4)
|
||||||
|
|
||||||
|
h2, err := m.Hash()
|
||||||
|
c.Assert(err, qt.IsNil)
|
||||||
|
|
||||||
|
c.Assert(h1, qt.Not(qt.Equals), h2)
|
||||||
|
|
||||||
|
m = NewOrdered[string, int]()
|
||||||
|
m.Set("b", 2)
|
||||||
|
m.Set("a", 1)
|
||||||
|
m.Set("c", 3)
|
||||||
|
|
||||||
|
h3, err := m.Hash()
|
||||||
|
c.Assert(err, qt.IsNil)
|
||||||
|
// Order does not matter.
|
||||||
|
c.Assert(h1, qt.Equals, h3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOrderedNil(t *testing.T) {
|
||||||
|
c := qt.New(t)
|
||||||
|
|
||||||
|
var m *Ordered[string, int]
|
||||||
|
|
||||||
|
m.Set("a", 1)
|
||||||
|
c.Assert(m.Keys(), qt.IsNil)
|
||||||
|
c.Assert(m.Values(), qt.IsNil)
|
||||||
|
v, found := m.Get("a")
|
||||||
|
c.Assert(found, qt.Equals, false)
|
||||||
|
c.Assert(v, qt.Equals, 0)
|
||||||
|
m.Delete("a")
|
||||||
|
var b bool
|
||||||
|
m.Range(func(k string, v int) bool {
|
||||||
|
b = true
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
c.Assert(b, qt.Equals, false)
|
||||||
|
c.Assert(m.Len(), qt.Equals, 0)
|
||||||
|
c.Assert(m.Clone(), qt.IsNil)
|
||||||
|
h, err := m.Hash()
|
||||||
|
c.Assert(err, qt.IsNil)
|
||||||
|
c.Assert(h, qt.Equals, uint64(0))
|
||||||
|
}
|
|
@ -143,7 +143,7 @@ type Config struct {
|
||||||
|
|
||||||
// The cascade configuration section contains the top level front matter cascade configuration options,
|
// The cascade configuration section contains the top level front matter cascade configuration options,
|
||||||
// a slice of page matcher and params to apply to those pages.
|
// a slice of page matcher and params to apply to those pages.
|
||||||
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, map[page.PageMatcher]maps.Params] `mapstructure:"-"`
|
Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, *maps.Ordered[page.PageMatcher, maps.Params]] `mapstructure:"-"`
|
||||||
|
|
||||||
// The segments defines segments for the site. Used for partial/segmented builds.
|
// The segments defines segments for the site. Used for partial/segmented builds.
|
||||||
Segments *config.ConfigNamespace[map[string]segments.SegmentConfig, segments.Segments] `mapstructure:"-"`
|
Segments *config.ConfigNamespace[map[string]segments.SegmentConfig, segments.Segments] `mapstructure:"-"`
|
||||||
|
@ -766,9 +766,10 @@ type Configs struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Configs) Validate(logger loggers.Logger) error {
|
func (c *Configs) Validate(logger loggers.Logger) error {
|
||||||
for p := range c.Base.Cascade.Config {
|
c.Base.Cascade.Config.Range(func(p page.PageMatcher, params maps.Params) bool {
|
||||||
page.CheckCascadePattern(logger, p)
|
page.CheckCascadePattern(logger, p)
|
||||||
}
|
return true
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -841,3 +841,38 @@ title: p1
|
||||||
b.AssertFileExists("public/s1/index.html", false)
|
b.AssertFileExists("public/s1/index.html", false)
|
||||||
b.AssertFileExists("public/s1/p1/index.html", false)
|
b.AssertFileExists("public/s1/p1/index.html", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue 12594.
|
||||||
|
func TestCascadeOrder(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
files := `
|
||||||
|
-- hugo.toml --
|
||||||
|
disableKinds = ['rss','sitemap','taxonomy','term', 'home']
|
||||||
|
-- content/_index.md --
|
||||||
|
---
|
||||||
|
title: Home
|
||||||
|
cascade:
|
||||||
|
- _target:
|
||||||
|
path: "**"
|
||||||
|
params:
|
||||||
|
background: yosemite.jpg
|
||||||
|
- _target:
|
||||||
|
params:
|
||||||
|
background: goldenbridge.jpg
|
||||||
|
---
|
||||||
|
-- content/p1.md --
|
||||||
|
---
|
||||||
|
title: p1
|
||||||
|
---
|
||||||
|
-- layouts/_default/single.html --
|
||||||
|
Background: {{ .Params.background }}|
|
||||||
|
-- layouts/_default/list.html --
|
||||||
|
{{ .Title }}|
|
||||||
|
`
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
b := Test(t, files)
|
||||||
|
b.AssertFileContent("public/p1/index.html", "Background: yosemite.jpg")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1387,7 +1387,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle cascades first to get any default dates set.
|
// Handle cascades first to get any default dates set.
|
||||||
var cascade map[page.PageMatcher]maps.Params
|
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
|
||||||
if keyPage == "" {
|
if keyPage == "" {
|
||||||
// Home page gets it's cascade from the site config.
|
// Home page gets it's cascade from the site config.
|
||||||
cascade = sa.conf.Cascade.Config
|
cascade = sa.conf.Cascade.Config
|
||||||
|
@ -1399,7 +1399,7 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
||||||
} else {
|
} else {
|
||||||
_, data := pw.WalkContext.Data().LongestPrefix(keyPage)
|
_, data := pw.WalkContext.Data().LongestPrefix(keyPage)
|
||||||
if data != nil {
|
if data != nil {
|
||||||
cascade = data.(map[page.PageMatcher]maps.Params)
|
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1481,11 +1481,11 @@ func (sa *sitePagesAssembler) applyAggregates() error {
|
||||||
pageResource := rs.r.(*pageState)
|
pageResource := rs.r.(*pageState)
|
||||||
relPath := pageResource.m.pathInfo.BaseRel(pageBundle.m.pathInfo)
|
relPath := pageResource.m.pathInfo.BaseRel(pageBundle.m.pathInfo)
|
||||||
pageResource.m.resourcePath = relPath
|
pageResource.m.resourcePath = relPath
|
||||||
var cascade map[page.PageMatcher]maps.Params
|
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
|
||||||
// Apply cascade (if set) to the page.
|
// Apply cascade (if set) to the page.
|
||||||
_, data := pw.WalkContext.Data().LongestPrefix(resourceKey)
|
_, data := pw.WalkContext.Data().LongestPrefix(resourceKey)
|
||||||
if data != nil {
|
if data != nil {
|
||||||
cascade = data.(map[page.PageMatcher]maps.Params)
|
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
|
||||||
}
|
}
|
||||||
if err := pageResource.setMetaPost(cascade); err != nil {
|
if err := pageResource.setMetaPost(cascade); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -1549,10 +1549,10 @@ func (sa *sitePagesAssembler) applyAggregatesToTaxonomiesAndTerms() error {
|
||||||
const eventName = "dates"
|
const eventName = "dates"
|
||||||
|
|
||||||
if p.Kind() == kinds.KindTerm {
|
if p.Kind() == kinds.KindTerm {
|
||||||
var cascade map[page.PageMatcher]maps.Params
|
var cascade *maps.Ordered[page.PageMatcher, maps.Params]
|
||||||
_, data := pw.WalkContext.Data().LongestPrefix(s)
|
_, data := pw.WalkContext.Data().LongestPrefix(s)
|
||||||
if data != nil {
|
if data != nil {
|
||||||
cascade = data.(map[page.PageMatcher]maps.Params)
|
cascade = data.(*maps.Ordered[page.PageMatcher, maps.Params])
|
||||||
}
|
}
|
||||||
if err := p.setMetaPost(cascade); err != nil {
|
if err := p.setMetaPost(cascade); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
|
@ -87,8 +87,8 @@ type pageMetaParams struct {
|
||||||
|
|
||||||
// These are only set in watch mode.
|
// These are only set in watch mode.
|
||||||
datesOriginal pagemeta.Dates
|
datesOriginal pagemeta.Dates
|
||||||
paramsOriginal map[string]any // contains the original params as defined in the front matter.
|
paramsOriginal map[string]any // contains the original params as defined in the front matter.
|
||||||
cascadeOriginal map[page.PageMatcher]maps.Params // contains the original cascade as defined in the front matter.
|
cascadeOriginal *maps.Ordered[page.PageMatcher, maps.Params] // contains the original cascade as defined in the front matter.
|
||||||
}
|
}
|
||||||
|
|
||||||
// From page front matter.
|
// From page front matter.
|
||||||
|
@ -96,10 +96,10 @@ type pageMetaFrontMatter struct {
|
||||||
configuredOutputFormats output.Formats // outputs defined in front matter.
|
configuredOutputFormats output.Formats // outputs defined in front matter.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *pageMetaParams) init(preserveOringal bool) {
|
func (m *pageMetaParams) init(preserveOriginal bool) {
|
||||||
if preserveOringal {
|
if preserveOriginal {
|
||||||
m.paramsOriginal = xmaps.Clone[maps.Params](m.pageConfig.Params)
|
m.paramsOriginal = xmaps.Clone[maps.Params](m.pageConfig.Params)
|
||||||
m.cascadeOriginal = xmaps.Clone[map[page.PageMatcher]maps.Params](m.pageConfig.CascadeCompiled)
|
m.cascadeOriginal = m.pageConfig.CascadeCompiled.Clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,22 +306,22 @@ func (p *pageMeta) setMetaPre(pi *contentParseInfo, logger loggers.Logger, conf
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error {
|
func (ps *pageState) setMetaPost(cascade *maps.Ordered[page.PageMatcher, maps.Params]) error {
|
||||||
ps.m.setMetaPostCount++
|
ps.m.setMetaPostCount++
|
||||||
var cascadeHashPre uint64
|
var cascadeHashPre uint64
|
||||||
if ps.m.setMetaPostCount > 1 {
|
if ps.m.setMetaPostCount > 1 {
|
||||||
cascadeHashPre = hashing.HashUint64(ps.m.pageConfig.CascadeCompiled)
|
cascadeHashPre = hashing.HashUint64(ps.m.pageConfig.CascadeCompiled)
|
||||||
ps.m.pageConfig.CascadeCompiled = xmaps.Clone[map[page.PageMatcher]maps.Params](ps.m.cascadeOriginal)
|
ps.m.pageConfig.CascadeCompiled = ps.m.cascadeOriginal.Clone()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply cascades first so they can be overridden later.
|
// Apply cascades first so they can be overridden later.
|
||||||
if cascade != nil {
|
if cascade != nil {
|
||||||
if ps.m.pageConfig.CascadeCompiled != nil {
|
if ps.m.pageConfig.CascadeCompiled != nil {
|
||||||
for k, v := range cascade {
|
cascade.Range(func(k page.PageMatcher, v maps.Params) bool {
|
||||||
vv, found := ps.m.pageConfig.CascadeCompiled[k]
|
vv, found := ps.m.pageConfig.CascadeCompiled.Get(k)
|
||||||
if !found {
|
if !found {
|
||||||
ps.m.pageConfig.CascadeCompiled[k] = v
|
ps.m.pageConfig.CascadeCompiled.Set(k, v)
|
||||||
} else {
|
} else {
|
||||||
// Merge
|
// Merge
|
||||||
for ck, cv := range v {
|
for ck, cv := range v {
|
||||||
|
@ -330,7 +330,8 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return true
|
||||||
|
})
|
||||||
cascade = ps.m.pageConfig.CascadeCompiled
|
cascade = ps.m.pageConfig.CascadeCompiled
|
||||||
} else {
|
} else {
|
||||||
ps.m.pageConfig.CascadeCompiled = cascade
|
ps.m.pageConfig.CascadeCompiled = cascade
|
||||||
|
@ -354,16 +355,17 @@ func (ps *pageState) setMetaPost(cascade map[page.PageMatcher]maps.Params) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cascade is also applied to itself.
|
// Cascade is also applied to itself.
|
||||||
for m, v := range cascade {
|
cascade.Range(func(k page.PageMatcher, v maps.Params) bool {
|
||||||
if !m.Matches(ps) {
|
if !k.Matches(ps) {
|
||||||
continue
|
return true
|
||||||
}
|
}
|
||||||
for kk, vv := range v {
|
for kk, vv := range v {
|
||||||
if _, found := ps.m.pageConfig.Params[kk]; !found {
|
if _, found := ps.m.pageConfig.Params[kk]; !found {
|
||||||
ps.m.pageConfig.Params[kk] = vv
|
ps.m.pageConfig.Params[kk] = vv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
if err := ps.setMetaPostParams(); err != nil {
|
if err := ps.setMetaPostParams(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -104,9 +104,9 @@ func CheckCascadePattern(logger loggers.Logger, m PageMatcher) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace[[]PageMatcherParamsConfig, map[PageMatcher]maps.Params], error) {
|
func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, maps.Params]], error) {
|
||||||
buildConfig := func(in any) (map[PageMatcher]maps.Params, any, error) {
|
buildConfig := func(in any) (*maps.Ordered[PageMatcher, maps.Params], any, error) {
|
||||||
cascade := make(map[PageMatcher]maps.Params)
|
cascade := maps.NewOrdered[PageMatcher, maps.Params]()
|
||||||
if in == nil {
|
if in == nil {
|
||||||
return cascade, []map[string]any{}, nil
|
return cascade, []map[string]any{}, nil
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace
|
||||||
for _, cfg := range cfgs {
|
for _, cfg := range cfgs {
|
||||||
m := cfg.Target
|
m := cfg.Target
|
||||||
CheckCascadePattern(logger, m)
|
CheckCascadePattern(logger, m)
|
||||||
c, found := cascade[m]
|
c, found := cascade.Get(m)
|
||||||
if found {
|
if found {
|
||||||
// Merge
|
// Merge
|
||||||
for k, v := range cfg.Params {
|
for k, v := range cfg.Params {
|
||||||
|
@ -143,18 +143,18 @@ func DecodeCascadeConfig(logger loggers.Logger, in any) (*config.ConfigNamespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cascade[m] = cfg.Params
|
cascade.Set(m, cfg.Params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cascade, cfgs, nil
|
return cascade, cfgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return config.DecodeNamespace[[]PageMatcherParamsConfig](in, buildConfig)
|
return config.DecodeNamespace[[]PageMatcherParamsConfig, *maps.Ordered[PageMatcher, maps.Params]](in, buildConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeCascade decodes in which could be either a map or a slice of maps.
|
// DecodeCascade decodes in which could be either a map or a slice of maps.
|
||||||
func DecodeCascade(logger loggers.Logger, in any) (map[PageMatcher]maps.Params, error) {
|
func DecodeCascade(logger loggers.Logger, in any) (*maps.Ordered[PageMatcher, maps.Params], error) {
|
||||||
conf, err := DecodeCascadeConfig(logger, in)
|
conf, err := DecodeCascadeConfig(logger, in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -133,16 +133,8 @@ func TestDecodeCascadeConfig(t *testing.T) {
|
||||||
|
|
||||||
c.Assert(err, qt.IsNil)
|
c.Assert(err, qt.IsNil)
|
||||||
c.Assert(got, qt.IsNotNil)
|
c.Assert(got, qt.IsNotNil)
|
||||||
c.Assert(got.Config, qt.DeepEquals,
|
c.Assert(got.Config.Keys(), qt.DeepEquals, []PageMatcher{{Kind: "page", Environment: "production"}, {Kind: "page"}})
|
||||||
map[PageMatcher]maps.Params{
|
c.Assert(got.Config.Values(), qt.DeepEquals, []maps.Params{{"a": string("av")}, {"b": string("bv")}})
|
||||||
{Path: "", Kind: "page", Lang: "", Environment: ""}: {
|
|
||||||
"b": "bv",
|
|
||||||
},
|
|
||||||
{Path: "", Kind: "page", Lang: "", Environment: "production"}: {
|
|
||||||
"a": "av",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
c.Assert(got.SourceStructure, qt.DeepEquals, []PageMatcherParamsConfig{
|
c.Assert(got.SourceStructure, qt.DeepEquals, []PageMatcherParamsConfig{
|
||||||
{
|
{
|
||||||
Params: maps.Params{"a": string("av")},
|
Params: maps.Params{"a": string("av")},
|
||||||
|
|
|
@ -114,9 +114,9 @@ type PageConfig struct {
|
||||||
Content Source
|
Content Source
|
||||||
|
|
||||||
// Compiled values.
|
// Compiled values.
|
||||||
CascadeCompiled map[page.PageMatcher]maps.Params
|
CascadeCompiled *maps.Ordered[page.PageMatcher, maps.Params] `mapstructure:"-" json:"-"`
|
||||||
ContentMediaType media.Type `mapstructure:"-" json:"-"`
|
ContentMediaType media.Type `mapstructure:"-" json:"-"`
|
||||||
IsFromContentAdapter bool `mapstructure:"-" json:"-"`
|
IsFromContentAdapter bool `mapstructure:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultPageConfig = PageConfig{
|
var DefaultPageConfig = PageConfig{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue