mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
Fix Params case handling in the index, sort and where func
This means that you can now do: ``` {{ range where .Site.Pages "Params.MYPARAM" "foo" }} ```
This commit is contained in:
parent
cd07e6d57b
commit
a3fe5e5e35
33 changed files with 317 additions and 155 deletions
|
@ -30,12 +30,12 @@ import (
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/parser/metadecoders"
|
"github.com/gohugoio/hugo/parser/metadecoders"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
"github.com/gohugoio/hugo/helpers"
|
"github.com/gohugoio/hugo/helpers"
|
||||||
"github.com/gohugoio/hugo/hugofs"
|
"github.com/gohugoio/hugo/hugofs"
|
||||||
"github.com/gohugoio/hugo/hugolib"
|
"github.com/gohugoio/hugo/hugolib"
|
||||||
"github.com/gohugoio/hugo/parser"
|
"github.com/gohugoio/hugo/parser"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/spf13/cast"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
jww "github.com/spf13/jwalterweatherman"
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
)
|
)
|
||||||
|
@ -420,7 +420,7 @@ func convertJekyllPost(s *hugolib.Site, path, relPath, targetDir string, draft b
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertJekyllMetaData(m interface{}, postName string, postDate time.Time, draft bool) (interface{}, error) {
|
func convertJekyllMetaData(m interface{}, postName string, postDate time.Time, draft bool) (interface{}, error) {
|
||||||
metadata, err := cast.ToStringMapE(m)
|
metadata, err := maps.ToStringMapE(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -472,7 +472,7 @@ func convertJekyllMetaData(m interface{}, postName string, postDate time.Time, d
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertJekyllContent(m interface{}, content string) string {
|
func convertJekyllContent(m interface{}, content string) string {
|
||||||
metadata, _ := cast.ToStringMapE(m)
|
metadata, _ := maps.ToStringMapE(m)
|
||||||
|
|
||||||
lines := strings.Split(content, "\n")
|
lines := strings.Split(content, "\n")
|
||||||
var resultLines []string
|
var resultLines []string
|
||||||
|
|
|
@ -25,26 +25,45 @@ import (
|
||||||
// recursively.
|
// recursively.
|
||||||
// Notes:
|
// Notes:
|
||||||
// * This will modify the map given.
|
// * This will modify the map given.
|
||||||
// * Any nested map[interface{}]interface{} will be converted to map[string]interface{}.
|
// * Any nested map[interface{}]interface{} will be converted to Params.
|
||||||
func ToLower(m map[string]interface{}) {
|
func ToLower(m Params) {
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
|
var retyped bool
|
||||||
switch v.(type) {
|
switch v.(type) {
|
||||||
case map[interface{}]interface{}:
|
case map[interface{}]interface{}:
|
||||||
v = cast.ToStringMap(v)
|
var p Params = cast.ToStringMap(v)
|
||||||
ToLower(v.(map[string]interface{}))
|
v = p
|
||||||
|
ToLower(p)
|
||||||
|
retyped = true
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
ToLower(v.(map[string]interface{}))
|
var p Params = v.(map[string]interface{})
|
||||||
|
v = p
|
||||||
|
ToLower(p)
|
||||||
|
retyped = true
|
||||||
}
|
}
|
||||||
|
|
||||||
lKey := strings.ToLower(k)
|
lKey := strings.ToLower(k)
|
||||||
if k != lKey {
|
if retyped || k != lKey {
|
||||||
delete(m, k)
|
delete(m, k)
|
||||||
m[lKey] = v
|
m[lKey] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ToStringMapE(in interface{}) (map[string]interface{}, error) {
|
||||||
|
switch in.(type) {
|
||||||
|
case Params:
|
||||||
|
return in.(Params), nil
|
||||||
|
default:
|
||||||
|
return cast.ToStringMapE(in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToStringMap(in interface{}) map[string]interface{} {
|
||||||
|
m, _ := ToStringMapE(in)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
type keyRename struct {
|
type keyRename struct {
|
||||||
pattern glob.Glob
|
pattern glob.Glob
|
||||||
newKey string
|
newKey string
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
package maps
|
package maps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -21,7 +22,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestToLower(t *testing.T) {
|
func TestToLower(t *testing.T) {
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input map[string]interface{}
|
input map[string]interface{}
|
||||||
expected map[string]interface{}
|
expected map[string]interface{}
|
||||||
|
@ -30,7 +30,7 @@ func TestToLower(t *testing.T) {
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"abC": 32,
|
"abC": 32,
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
Params{
|
||||||
"abc": 32,
|
"abc": 32,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -48,16 +48,16 @@ func TestToLower(t *testing.T) {
|
||||||
"J": 25,
|
"J": 25,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
Params{
|
||||||
"abc": 32,
|
"abc": 32,
|
||||||
"def": map[string]interface{}{
|
"def": Params{
|
||||||
"23": "A value",
|
"23": "A value",
|
||||||
"24": map[string]interface{}{
|
"24": Params{
|
||||||
"abcde": "A value",
|
"abcde": "A value",
|
||||||
"efghi": "Another value",
|
"efghi": "Another value",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"ghi": map[string]interface{}{
|
"ghi": Params{
|
||||||
"j": 25,
|
"j": 25,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -65,11 +65,13 @@ func TestToLower(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
// ToLower modifies input.
|
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||||
ToLower(test.input)
|
// ToLower modifies input.
|
||||||
if !reflect.DeepEqual(test.expected, test.input) {
|
ToLower(test.input)
|
||||||
t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input)
|
if !reflect.DeepEqual(test.expected, test.input) {
|
||||||
}
|
t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,76 +19,89 @@ import (
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Params is a map where all keys are lower case.
|
||||||
|
type Params map[string]interface{}
|
||||||
|
|
||||||
|
// Get does a lower case and nested search in this map.
|
||||||
|
// It will return nil if none found.
|
||||||
|
func (p Params) Get(indices ...string) interface{} {
|
||||||
|
v, _, _ := getNested(p, indices)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNested(m map[string]interface{}, indices []string) (interface{}, string, map[string]interface{}) {
|
||||||
|
if len(indices) == 0 {
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
first := indices[0]
|
||||||
|
v, found := m[strings.ToLower(cast.ToString(first))]
|
||||||
|
if !found {
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(indices) == 1 {
|
||||||
|
return v, first, m
|
||||||
|
}
|
||||||
|
|
||||||
|
switch m2 := v.(type) {
|
||||||
|
case Params:
|
||||||
|
return getNested(m2, indices[1:])
|
||||||
|
case map[string]interface{}:
|
||||||
|
return getNested(m2, indices[1:])
|
||||||
|
default:
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetNestedParam gets the first match of the keyStr in the candidates given.
|
// GetNestedParam gets the first match of the keyStr in the candidates given.
|
||||||
// It will first try the exact match and then try to find it as a nested map value,
|
// It will first try the exact match and then try to find it as a nested map value,
|
||||||
// using the given separator, e.g. "mymap.name".
|
// using the given separator, e.g. "mymap.name".
|
||||||
// It assumes that all the maps given have lower cased keys.
|
// It assumes that all the maps given have lower cased keys.
|
||||||
func GetNestedParam(keyStr, separator string, candidates ...map[string]interface{}) (interface{}, error) {
|
func GetNestedParam(keyStr, separator string, candidates ...Params) (interface{}, error) {
|
||||||
keyStr = strings.ToLower(keyStr)
|
keyStr = strings.ToLower(keyStr)
|
||||||
|
|
||||||
lookupFn := func(key string) interface{} {
|
// Try exact match first
|
||||||
for _, m := range candidates {
|
for _, m := range candidates {
|
||||||
if v, ok := m[key]; ok {
|
if v, ok := m[keyStr]; ok {
|
||||||
return v
|
return v, nil
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
v, _, _, err := GetNestedParamFn(keyStr, separator, lookupFn)
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetNestedParamFn(keyStr, separator string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) {
|
|
||||||
result, _ := traverseDirectParams(keyStr, lookupFn)
|
|
||||||
if result != nil {
|
|
||||||
return result, keyStr, nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
keySegments := strings.Split(keyStr, separator)
|
keySegments := strings.Split(keyStr, separator)
|
||||||
if len(keySegments) == 1 {
|
for _, m := range candidates {
|
||||||
return nil, keyStr, nil, nil
|
if v := m.Get(keySegments...); v != nil {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return traverseNestedParams(keySegments, lookupFn)
|
return nil, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func traverseDirectParams(keyStr string, lookupFn func(key string) interface{}) (interface{}, error) {
|
func GetNestedParamFn(keyStr, separator string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) {
|
||||||
return lookupFn(keyStr), nil
|
keySegments := strings.Split(strings.ToLower(keyStr), separator)
|
||||||
}
|
if len(keySegments) == 0 {
|
||||||
|
|
||||||
func traverseNestedParams(keySegments []string, lookupFn func(key string) interface{}) (interface{}, string, map[string]interface{}, error) {
|
|
||||||
firstKey, rest := keySegments[0], keySegments[1:]
|
|
||||||
result := lookupFn(firstKey)
|
|
||||||
if result == nil || len(rest) == 0 {
|
|
||||||
return result, firstKey, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch m := result.(type) {
|
|
||||||
case map[string]interface{}:
|
|
||||||
v, key, owner := traverseParams(rest, m)
|
|
||||||
return v, key, owner, nil
|
|
||||||
default:
|
|
||||||
return nil, "", nil, nil
|
return nil, "", nil, nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func traverseParams(keys []string, m map[string]interface{}) (interface{}, string, map[string]interface{}) {
|
first := lookupFn(keySegments[0])
|
||||||
// Shift first element off.
|
if first == nil {
|
||||||
firstKey, rest := keys[0], keys[1:]
|
return nil, "", nil, nil
|
||||||
result := m[firstKey]
|
|
||||||
|
|
||||||
// No point in continuing here.
|
|
||||||
if result == nil {
|
|
||||||
return result, "", nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rest) == 0 {
|
if len(keySegments) == 1 {
|
||||||
// That was the last key.
|
return first, keySegments[0], nil, nil
|
||||||
return result, firstKey, m
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// That was not the last key.
|
switch m := first.(type) {
|
||||||
return traverseParams(rest, cast.ToStringMap(result))
|
case map[string]interface{}:
|
||||||
|
v, key, owner := getNested(m, keySegments[1:])
|
||||||
|
return v, key, owner, nil
|
||||||
|
case Params:
|
||||||
|
v, key, owner := getNested(m, keySegments[1:])
|
||||||
|
return v, key, owner, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, "", nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ func TestGetNestedParam(t *testing.T) {
|
||||||
|
|
||||||
c := qt.New(t)
|
c := qt.New(t)
|
||||||
|
|
||||||
must := func(keyStr, separator string, candidates ...map[string]interface{}) interface{} {
|
must := func(keyStr, separator string, candidates ...Params) interface{} {
|
||||||
v, err := GetNestedParam(keyStr, separator, candidates...)
|
v, err := GetNestedParam(keyStr, separator, candidates...)
|
||||||
c.Assert(err, qt.IsNil)
|
c.Assert(err, qt.IsNil)
|
||||||
return v
|
return v
|
||||||
|
|
|
@ -37,8 +37,8 @@ type Runner interface {
|
||||||
|
|
||||||
type errGroupRunner struct {
|
type errGroupRunner struct {
|
||||||
*errgroup.Group
|
*errgroup.Group
|
||||||
w *Workers
|
w *Workers
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *errGroupRunner) Run(fn func() error) {
|
func (g *errGroupRunner) Run(fn func() error) {
|
||||||
|
@ -68,6 +68,6 @@ func (w *Workers) Start(ctx context.Context) (Runner, context.Context) {
|
||||||
return &errGroupRunner{
|
return &errGroupRunner{
|
||||||
Group: g,
|
Group: g,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
w: w,
|
w: w,
|
||||||
}, ctx
|
}, ctx
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ package para
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
@ -25,6 +26,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPara(t *testing.T) {
|
func TestPara(t *testing.T) {
|
||||||
|
if runtime.NumCPU() < 4 {
|
||||||
|
t.Skipf("skip para test, CPU count is %d", runtime.NumCPU())
|
||||||
|
}
|
||||||
|
|
||||||
c := qt.New(t)
|
c := qt.New(t)
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ angledQuotes = false
|
||||||
hrefTargetBlank = false
|
hrefTargetBlank = false
|
||||||
[Languages.en.Colors]
|
[Languages.en.Colors]
|
||||||
BLUE = "blues"
|
BLUE = "blues"
|
||||||
yellow = "golden"
|
Yellow = "golden"
|
||||||
`
|
`
|
||||||
caseMixingPage1En = `
|
caseMixingPage1En = `
|
||||||
---
|
---
|
||||||
|
@ -137,18 +137,6 @@ func TestCaseInsensitiveConfigurationVariations(t *testing.T) {
|
||||||
|
|
||||||
c := qt.New(t)
|
c := qt.New(t)
|
||||||
|
|
||||||
// See issues 2615, 1129, 2590 and maybe some others
|
|
||||||
// Also see 2598
|
|
||||||
//
|
|
||||||
// Viper is now, at least for the Hugo part, case insensitive
|
|
||||||
// So we need tests for all of it, with needed adjustments on the Hugo side.
|
|
||||||
// Not sure what that will be. Let us see.
|
|
||||||
|
|
||||||
// So all the below with case variations:
|
|
||||||
// config: regular fields, blackfriday config, param with nested map
|
|
||||||
// language: new and overridden values, in regular fields and nested paramsmap
|
|
||||||
// page frontmatter: regular fields, blackfriday config, param with nested map
|
|
||||||
|
|
||||||
mm := afero.NewMemMapFs()
|
mm := afero.NewMemMapFs()
|
||||||
|
|
||||||
caseMixingTestsWriteCommonSources(t, mm)
|
caseMixingTestsWriteCommonSources(t, mm)
|
||||||
|
@ -168,17 +156,27 @@ Block Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }}
|
||||||
{{ define "main"}}
|
{{ define "main"}}
|
||||||
Page Colors: {{ .Params.CoLOR }}|{{ .Params.Colors.Blue }}
|
Page Colors: {{ .Params.CoLOR }}|{{ .Params.Colors.Blue }}
|
||||||
Site Colors: {{ .Site.Params.COlOR }}|{{ .Site.Params.COLORS.YELLOW }}
|
Site Colors: {{ .Site.Params.COlOR }}|{{ .Site.Params.COLORS.YELLOW }}
|
||||||
|
{{ template "index-color" (dict "name" "Page" "params" .Params) }}
|
||||||
|
{{ template "index-color" (dict "name" "Site" "params" .Site.Params) }}
|
||||||
|
|
||||||
{{ .Content }}
|
{{ .Content }}
|
||||||
{{ partial "partial.html" . }}
|
{{ partial "partial.html" . }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ define "index-color" }}
|
||||||
|
{{ $yellow := index .params "COLoRS" "yELLOW" }}
|
||||||
|
{{ $colors := index .params "COLoRS" }}
|
||||||
|
{{ $yellow2 := index $colors "yEllow" }}
|
||||||
|
index1|{{ .name }}: {{ $yellow }}|
|
||||||
|
index2|{{ .name }}: {{ $yellow2 }}|
|
||||||
|
{{ end }}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `
|
writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `
|
||||||
Page Title: {{ .Title }}
|
Page Title: {{ .Title }}
|
||||||
Site Title: {{ .Site.Title }}
|
Site Title: {{ .Site.Title }}
|
||||||
Site Lang Mood: {{ .Site.Language.Params.MOoD }}
|
Site Lang Mood: {{ .Site.Language.Params.MOoD }}
|
||||||
Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }}
|
Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }}|{{ index .Params "ColOR" }}
|
||||||
Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}
|
Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}|{{ index .Site.Params "ColOR" }}
|
||||||
{{ $page2 := .Site.GetPage "/sect2/page2" }}
|
{{ $page2 := .Site.GetPage "/sect2/page2" }}
|
||||||
{{ if $page2 }}
|
{{ if $page2 }}
|
||||||
Page2: {{ $page2.Params.ColoR }}
|
Page2: {{ $page2.Params.ColoR }}
|
||||||
|
@ -200,8 +198,8 @@ Page2: {{ $page2.Params.ColoR }}
|
||||||
}
|
}
|
||||||
|
|
||||||
th.assertFileContent(filepath.Join("public", "nn", "sect1", "page1", "index.html"),
|
th.assertFileContent(filepath.Join("public", "nn", "sect1", "page1", "index.html"),
|
||||||
"Page Colors: red|heavenly",
|
"Page Colors: red|heavenly|red",
|
||||||
"Site Colors: green|yellow",
|
"Site Colors: green|yellow|green",
|
||||||
"Site Lang Mood: Happy",
|
"Site Lang Mood: Happy",
|
||||||
"Shortcode Page: red|heavenly",
|
"Shortcode Page: red|heavenly",
|
||||||
"Shortcode Site: green|yellow",
|
"Shortcode Site: green|yellow",
|
||||||
|
@ -230,6 +228,10 @@ Page2: {{ $page2.Params.ColoR }}
|
||||||
"Block Page Colors: black|sky",
|
"Block Page Colors: black|sky",
|
||||||
"Partial Page: black|sky",
|
"Partial Page: black|sky",
|
||||||
"Partial Site: green|yellow",
|
"Partial Site: green|yellow",
|
||||||
|
"index1|Page: flower|",
|
||||||
|
"index1|Site: yellow|",
|
||||||
|
"index2|Page: flower|",
|
||||||
|
"index2|Site: yellow|",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -228,7 +228,7 @@ func (p *pageMeta) Param(key interface{}) (interface{}, error) {
|
||||||
return resource.Param(p, p.s.Info.Params(), key)
|
return resource.Param(p, p.s.Info.Params(), key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pageMeta) Params() map[string]interface{} {
|
func (p *pageMeta) Params() maps.Params {
|
||||||
return p.params
|
return p.params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +312,7 @@ func (pm *pageMeta) setMetadata(bucket *pagesMapBucket, p *pageState, frontmatte
|
||||||
return errors.New("missing frontmatter data")
|
return errors.New("missing frontmatter data")
|
||||||
}
|
}
|
||||||
|
|
||||||
pm.params = make(map[string]interface{})
|
pm.params = make(maps.Params)
|
||||||
|
|
||||||
if frontmatter != nil {
|
if frontmatter != nil {
|
||||||
// Needed for case insensitive fetching of params values
|
// Needed for case insensitive fetching of params values
|
||||||
|
@ -320,7 +320,7 @@ func (pm *pageMeta) setMetadata(bucket *pagesMapBucket, p *pageState, frontmatte
|
||||||
if p.IsNode() {
|
if p.IsNode() {
|
||||||
// Check for any cascade define on itself.
|
// Check for any cascade define on itself.
|
||||||
if cv, found := frontmatter["cascade"]; found {
|
if cv, found := frontmatter["cascade"]; found {
|
||||||
cvm := cast.ToStringMap(cv)
|
cvm := maps.ToStringMap(cv)
|
||||||
if bucket.cascade == nil {
|
if bucket.cascade == nil {
|
||||||
bucket.cascade = cvm
|
bucket.cascade = cvm
|
||||||
} else {
|
} else {
|
||||||
|
@ -479,7 +479,7 @@ func (pm *pageMeta) setMetadata(bucket *pagesMapBucket, p *pageState, frontmatte
|
||||||
}
|
}
|
||||||
pm.params[loki] = pm.aliases
|
pm.params[loki] = pm.aliases
|
||||||
case "sitemap":
|
case "sitemap":
|
||||||
p.m.sitemap = config.DecodeSitemap(p.s.siteCfg.sitemap, cast.ToStringMap(v))
|
p.m.sitemap = config.DecodeSitemap(p.s.siteCfg.sitemap, maps.ToStringMap(v))
|
||||||
pm.params[loki] = p.m.sitemap
|
pm.params[loki] = p.m.sitemap
|
||||||
sitemapSet = true
|
sitemapSet = true
|
||||||
case "iscjklanguage":
|
case "iscjklanguage":
|
||||||
|
@ -495,7 +495,7 @@ func (pm *pageMeta) setMetadata(bucket *pagesMapBucket, p *pageState, frontmatte
|
||||||
switch vv := v.(type) {
|
switch vv := v.(type) {
|
||||||
case []map[interface{}]interface{}:
|
case []map[interface{}]interface{}:
|
||||||
for _, vvv := range vv {
|
for _, vvv := range vv {
|
||||||
resources = append(resources, cast.ToStringMap(vvv))
|
resources = append(resources, maps.ToStringMap(vvv))
|
||||||
}
|
}
|
||||||
case []map[string]interface{}:
|
case []map[string]interface{}:
|
||||||
resources = append(resources, vv...)
|
resources = append(resources, vv...)
|
||||||
|
@ -503,7 +503,7 @@ func (pm *pageMeta) setMetadata(bucket *pagesMapBucket, p *pageState, frontmatte
|
||||||
for _, vvv := range vv {
|
for _, vvv := range vv {
|
||||||
switch vvvv := vvv.(type) {
|
switch vvvv := vvv.(type) {
|
||||||
case map[interface{}]interface{}:
|
case map[interface{}]interface{}:
|
||||||
resources = append(resources, cast.ToStringMap(vvvv))
|
resources = append(resources, maps.ToStringMap(vvvv))
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
resources = append(resources, vvvv)
|
resources = append(resources, vvvv)
|
||||||
}
|
}
|
||||||
|
@ -642,7 +642,7 @@ func (p *pageMeta) applyDefaultValues() error {
|
||||||
var renderingConfigOverrides map[string]interface{}
|
var renderingConfigOverrides map[string]interface{}
|
||||||
bfParam := getParamToLower(p, "blackfriday")
|
bfParam := getParamToLower(p, "blackfriday")
|
||||||
if bfParam != nil {
|
if bfParam != nil {
|
||||||
renderingConfigOverrides = cast.ToStringMap(bfParam)
|
renderingConfigOverrides = maps.ToStringMap(bfParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
cp := p.s.ContentSpec.Converters.Get(p.markup)
|
cp := p.s.ContentSpec.Converters.Get(p.markup)
|
||||||
|
@ -705,14 +705,9 @@ func getParam(m resource.ResourceParamsProvider, key string, stringToLower bool)
|
||||||
return helpers.SliceToLower(val)
|
return helpers.SliceToLower(val)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
case map[string]interface{}: // JSON and TOML
|
default:
|
||||||
return v
|
|
||||||
case map[interface{}]interface{}: // YAML
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
//p.s.Log.ERROR.Printf("GetParam(\"%s\"): Unknown type %s\n", key, reflect.TypeOf(v))
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getParamToLower(m resource.ResourceParamsProvider, key string) interface{} {
|
func getParamToLower(m resource.ResourceParamsProvider, key string) interface{} {
|
||||||
|
|
|
@ -1573,7 +1573,8 @@ baseURL = "https://example.org"
|
||||||
{{ $withStringParam := .Site.GetPage "withstringparam" }}
|
{{ $withStringParam := .Site.GetPage "withstringparam" }}
|
||||||
|
|
||||||
Author page: {{ $withParam.Param "author.name" }}
|
Author page: {{ $withParam.Param "author.name" }}
|
||||||
Author page string: {{ $withStringParam.Param "author.name" }}|
|
Author name page string: {{ $withStringParam.Param "author.name" }}|
|
||||||
|
Author page string: {{ $withStringParam.Param "author" }}|
|
||||||
Author site config: {{ $noParam.Param "author.name" }}
|
Author site config: {{ $noParam.Param "author.name" }}
|
||||||
|
|
||||||
`,
|
`,
|
||||||
|
@ -1603,8 +1604,10 @@ author = "Jo Nesbø"
|
||||||
`)
|
`)
|
||||||
b.Build(BuildCfg{})
|
b.Build(BuildCfg{})
|
||||||
|
|
||||||
b.AssertFileContent("public/index.html", "Author page: Ernest Miller Hemingway")
|
b.AssertFileContent("public/index.html",
|
||||||
b.AssertFileContent("public/index.html", "Author page string: |")
|
"Author page: Ernest Miller Hemingway",
|
||||||
b.AssertFileContent("public/index.html", "Author site config: Kurt Vonnegut")
|
"Author name page string: Kurt Vonnegut|",
|
||||||
|
"Author page string: Jo Nesbø|",
|
||||||
|
"Author site config: Kurt Vonnegut")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
|
|
||||||
radix "github.com/armon/go-radix"
|
radix "github.com/armon/go-radix"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
|
|
||||||
|
@ -359,7 +361,7 @@ func (m *pagesMap) cleanKey(key string) string {
|
||||||
|
|
||||||
func (m *pagesMap) mergeCascades(b1, b2 *pagesMapBucket) {
|
func (m *pagesMap) mergeCascades(b1, b2 *pagesMapBucket) {
|
||||||
if b1.cascade == nil {
|
if b1.cascade == nil {
|
||||||
b1.cascade = make(map[string]interface{})
|
b1.cascade = make(maps.Params)
|
||||||
}
|
}
|
||||||
if b2 != nil && b2.cascade != nil {
|
if b2 != nil && b2.cascade != nil {
|
||||||
for k, v := range b2.cascade {
|
for k, v := range b2.cascade {
|
||||||
|
|
|
@ -28,6 +28,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/resources/resource"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/markup/converter"
|
"github.com/gohugoio/hugo/markup/converter"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/hugofs/files"
|
"github.com/gohugoio/hugo/hugofs/files"
|
||||||
|
@ -581,7 +583,7 @@ func (s *SiteInfo) Taxonomies() interface{} {
|
||||||
return s.s.Taxonomies
|
return s.s.Taxonomies
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SiteInfo) Params() map[string]interface{} {
|
func (s *SiteInfo) Params() maps.Params {
|
||||||
return s.s.Language().Params()
|
return s.s.Language().Params()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,14 +656,9 @@ type SiteSocial map[string]string
|
||||||
|
|
||||||
// Param is a convenience method to do lookups in SiteInfo's Params map.
|
// Param is a convenience method to do lookups in SiteInfo's Params map.
|
||||||
//
|
//
|
||||||
// This method is also implemented on Page and Node.
|
// This method is also implemented on Page.
|
||||||
func (s *SiteInfo) Param(key interface{}) (interface{}, error) {
|
func (s *SiteInfo) Param(key interface{}) (interface{}, error) {
|
||||||
keyStr, err := cast.ToStringE(key)
|
return resource.Param(s, nil, key)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
keyStr = strings.ToLower(keyStr)
|
|
||||||
return s.Params()[keyStr], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SiteInfo) IsMultiLingual() bool {
|
func (s *SiteInfo) IsMultiLingual() bool {
|
||||||
|
@ -1272,7 +1269,7 @@ func (s *Site) getMenusFromConfig() navigation.Menus {
|
||||||
s.Log.DEBUG.Printf("found menu: %q, in site config\n", name)
|
s.Log.DEBUG.Printf("found menu: %q, in site config\n", name)
|
||||||
|
|
||||||
menuEntry := navigation.MenuEntry{Menu: name}
|
menuEntry := navigation.MenuEntry{Menu: name}
|
||||||
ime, err := cast.ToStringMapE(entry)
|
ime, err := maps.ToStringMapE(entry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.ERROR.Printf("unable to process menus in site config\n")
|
s.Log.ERROR.Printf("unable to process menus in site config\n")
|
||||||
s.Log.ERROR.Println(err)
|
s.Log.ERROR.Println(err)
|
||||||
|
|
|
@ -171,7 +171,7 @@ func toSortedLanguages(cfg config.Provider, l map[string]interface{}) (Languages
|
||||||
i := 0
|
i := 0
|
||||||
|
|
||||||
for lang, langConf := range l {
|
for lang, langConf := range l {
|
||||||
langsMap, err := cast.ToStringMapE(langConf)
|
langsMap, err := maps.ToStringMapE(langConf)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Language config is not a map: %T", langConf)
|
return nil, fmt.Errorf("Language config is not a map: %T", langConf)
|
||||||
|
@ -192,7 +192,7 @@ func toSortedLanguages(cfg config.Provider, l map[string]interface{}) (Languages
|
||||||
case "disabled":
|
case "disabled":
|
||||||
language.Disabled = cast.ToBool(v)
|
language.Disabled = cast.ToBool(v)
|
||||||
case "params":
|
case "params":
|
||||||
m := cast.ToStringMap(v)
|
m := maps.ToStringMap(v)
|
||||||
// Needed for case insensitive fetching of params values
|
// Needed for case insensitive fetching of params values
|
||||||
maps.ToLower(m)
|
maps.ToLower(m)
|
||||||
for k, vv := range m {
|
for k, vv := range m {
|
||||||
|
|
|
@ -177,7 +177,7 @@ func (l *Language) GetInt(key string) int { return cast.ToInt(l.Get(key)) }
|
||||||
|
|
||||||
// GetStringMap returns the value associated with the key as a map of interfaces.
|
// GetStringMap returns the value associated with the key as a map of interfaces.
|
||||||
func (l *Language) GetStringMap(key string) map[string]interface{} {
|
func (l *Language) GetStringMap(key string) map[string]interface{} {
|
||||||
return cast.ToStringMap(l.Get(key))
|
return maps.ToStringMap(l.Get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStringMapString returns the value associated with the key as a map of strings.
|
// GetStringMapString returns the value associated with the key as a map of strings.
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
package navigation
|
package navigation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
"github.com/gohugoio/hugo/common/types"
|
"github.com/gohugoio/hugo/common/types"
|
||||||
"github.com/gohugoio/hugo/compare"
|
"github.com/gohugoio/hugo/compare"
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ type Page interface {
|
||||||
Section() string
|
Section() string
|
||||||
Weight() int
|
Weight() int
|
||||||
IsPage() bool
|
IsPage() bool
|
||||||
Params() map[string]interface{}
|
Params() maps.Params
|
||||||
}
|
}
|
||||||
|
|
||||||
// Menu is a collection of menu entries.
|
// Menu is a collection of menu entries.
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
package navigation
|
package navigation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
)
|
)
|
||||||
|
@ -73,7 +75,7 @@ func PageMenusFromPage(p Page) (PageMenus, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Could be a structured menu entry
|
// Could be a structured menu entry
|
||||||
menus, err := cast.ToStringMapE(ms)
|
menus, err := maps.ToStringMapE(ms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pm, errors.Wrapf(err, "unable to process menus for %q", p.LinkTitle())
|
return pm, errors.Wrapf(err, "unable to process menus for %q", p.LinkTitle())
|
||||||
}
|
}
|
||||||
|
@ -81,7 +83,7 @@ func PageMenusFromPage(p Page) (PageMenus, error) {
|
||||||
for name, menu := range menus {
|
for name, menu := range menus {
|
||||||
menuEntry := MenuEntry{Page: p, Name: p.LinkTitle(), Weight: p.Weight(), Menu: name}
|
menuEntry := MenuEntry{Page: p, Name: p.LinkTitle(), Weight: p.Weight(), Menu: name}
|
||||||
if menu != nil {
|
if menu != nil {
|
||||||
ime, err := cast.ToStringMapE(menu)
|
ime, err := maps.ToStringMapE(menu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pm, errors.Wrapf(err, "unable to process menus for %q", p.LinkTitle())
|
return pm, errors.Wrapf(err, "unable to process menus for %q", p.LinkTitle())
|
||||||
}
|
}
|
||||||
|
|
|
@ -300,7 +300,7 @@ func (p *nopPage) Param(key interface{}) (interface{}, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *nopPage) Params() map[string]interface{} {
|
func (p *nopPage) Params() maps.Params {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@ import (
|
||||||
"html/template"
|
"html/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/config"
|
"github.com/gohugoio/hugo/config"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/hugo"
|
"github.com/gohugoio/hugo/common/hugo"
|
||||||
|
@ -39,7 +41,7 @@ type Site interface {
|
||||||
Taxonomies() interface{}
|
Taxonomies() interface{}
|
||||||
LastChange() time.Time
|
LastChange() time.Time
|
||||||
Menus() navigation.Menus
|
Menus() navigation.Menus
|
||||||
Params() map[string]interface{}
|
Params() maps.Params
|
||||||
Data() map[string]interface{}
|
Data() map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +109,7 @@ func (t testSite) BaseURL() template.URL {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testSite) Params() map[string]interface{} {
|
func (t testSite) Params() maps.Params {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -370,7 +370,7 @@ func (p *testPage) Param(key interface{}) (interface{}, error) {
|
||||||
return resource.Param(p, nil, key)
|
return resource.Param(p, nil, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *testPage) Params() map[string]interface{} {
|
func (p *testPage) Params() maps.Params {
|
||||||
return p.params
|
return p.params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,9 +30,9 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/hugio"
|
"github.com/gohugoio/hugo/common/hugio"
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
"github.com/gohugoio/hugo/resources/page"
|
"github.com/gohugoio/hugo/resources/page"
|
||||||
"github.com/gohugoio/hugo/resources/resource"
|
"github.com/gohugoio/hugo/resources/resource"
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/helpers"
|
"github.com/gohugoio/hugo/helpers"
|
||||||
|
@ -228,7 +228,7 @@ func (l *genericResource) Name() string {
|
||||||
return l.name
|
return l.name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *genericResource) Params() map[string]interface{} {
|
func (l *genericResource) Params() maps.Params {
|
||||||
return l.params
|
return l.params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,12 +19,16 @@ import (
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Param(r ResourceParamsProvider, fallback map[string]interface{}, key interface{}) (interface{}, error) {
|
func Param(r ResourceParamsProvider, fallback maps.Params, key interface{}) (interface{}, error) {
|
||||||
keyStr, err := cast.ToStringE(key)
|
keyStr, err := cast.ToStringE(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fallback == nil {
|
||||||
|
return maps.GetNestedParam(keyStr, ".", r.Params())
|
||||||
|
}
|
||||||
|
|
||||||
return maps.GetNestedParam(keyStr, ".", r.Params(), fallback)
|
return maps.GetNestedParam(keyStr, ".", r.Params(), fallback)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
package resource
|
package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
"github.com/gohugoio/hugo/langs"
|
"github.com/gohugoio/hugo/langs"
|
||||||
"github.com/gohugoio/hugo/media"
|
"github.com/gohugoio/hugo/media"
|
||||||
"github.com/gohugoio/hugo/resources/images/exif"
|
"github.com/gohugoio/hugo/resources/images/exif"
|
||||||
|
@ -85,7 +86,7 @@ type ResourceMetaProvider interface {
|
||||||
|
|
||||||
type ResourceParamsProvider interface {
|
type ResourceParamsProvider interface {
|
||||||
// Params set in front matter for this resource.
|
// Params set in front matter for this resource.
|
||||||
Params() map[string]interface{}
|
Params() maps.Params
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResourceDataProvider interface {
|
type ResourceDataProvider interface {
|
||||||
|
|
|
@ -129,7 +129,7 @@ func AssignMetadata(metadata []map[string]interface{}, resources ...resource.Res
|
||||||
|
|
||||||
params, found := meta["params"]
|
params, found := meta["params"]
|
||||||
if found {
|
if found {
|
||||||
m := cast.ToStringMap(params)
|
m := maps.ToStringMap(params)
|
||||||
// Needed for case insensitive fetching of params values
|
// Needed for case insensitive fetching of params values
|
||||||
maps.ToLower(m)
|
maps.ToLower(m)
|
||||||
ma.updateParams(m)
|
ma.updateParams(m)
|
||||||
|
|
|
@ -26,11 +26,11 @@ import (
|
||||||
|
|
||||||
bp "github.com/gohugoio/hugo/bufferpool"
|
bp "github.com/gohugoio/hugo/bufferpool"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/resources/internal"
|
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/herrors"
|
"github.com/gohugoio/hugo/common/herrors"
|
||||||
"github.com/gohugoio/hugo/common/hugio"
|
"github.com/gohugoio/hugo/common/hugio"
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
"github.com/gohugoio/hugo/helpers"
|
"github.com/gohugoio/hugo/helpers"
|
||||||
|
"github.com/gohugoio/hugo/resources/internal"
|
||||||
"github.com/gohugoio/hugo/resources/resource"
|
"github.com/gohugoio/hugo/resources/resource"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/media"
|
"github.com/gohugoio/hugo/media"
|
||||||
|
@ -200,7 +200,7 @@ func (r *resourceAdapter) Name() string {
|
||||||
return r.target.Name()
|
return r.target.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resourceAdapter) Params() map[string]interface{} {
|
func (r *resourceAdapter) Params() maps.Params {
|
||||||
r.init(false, false)
|
r.init(false, false)
|
||||||
return r.target.Params()
|
return r.target.Params()
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
|
|
||||||
qt "github.com/frankban/quicktest"
|
qt "github.com/frankban/quicktest"
|
||||||
"github.com/gohugoio/hugo/common/loggers"
|
"github.com/gohugoio/hugo/common/loggers"
|
||||||
"github.com/gohugoio/hugo/config"
|
"github.com/gohugoio/hugo/config"
|
||||||
|
@ -891,6 +893,15 @@ type TstX struct {
|
||||||
unexported string
|
unexported string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TstParams struct {
|
||||||
|
params maps.Params
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x TstParams) Params() maps.Params {
|
||||||
|
return x.params
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
type TstXIHolder struct {
|
type TstXIHolder struct {
|
||||||
XI TstXI
|
XI TstXI
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,10 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/spf13/cast"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Index returns the result of indexing its first argument by the following
|
// Index returns the result of indexing its first argument by the following
|
||||||
|
@ -34,6 +38,11 @@ func (ns *Namespace) Index(item interface{}, args ...interface{}) (interface{},
|
||||||
return nil, errors.New("index of untyped nil")
|
return nil, errors.New("index of untyped nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lowerm, ok := item.(maps.Params)
|
||||||
|
if ok {
|
||||||
|
return lowerm.Get(cast.ToStringSlice(args)...), nil
|
||||||
|
}
|
||||||
|
|
||||||
var indices []interface{}
|
var indices []interface{}
|
||||||
|
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
|
@ -79,6 +88,7 @@ func (ns *Namespace) Index(item interface{}, args ...interface{}) (interface{},
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if x := v.MapIndex(index); x.IsValid() {
|
if x := v.MapIndex(index); x.IsValid() {
|
||||||
v = x
|
v = x
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -17,6 +17,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
|
|
||||||
qt "github.com/frankban/quicktest"
|
qt "github.com/frankban/quicktest"
|
||||||
"github.com/gohugoio/hugo/deps"
|
"github.com/gohugoio/hugo/deps"
|
||||||
)
|
)
|
||||||
|
@ -42,7 +44,8 @@ func TestIndex(t *testing.T) {
|
||||||
{[]map[string]map[string]string{{"a": {"b": "c"}}}, []interface{}{0, "a", "b"}, "c", false},
|
{[]map[string]map[string]string{{"a": {"b": "c"}}}, []interface{}{0, "a", "b"}, "c", false},
|
||||||
{map[string]map[string]interface{}{"a": {"b": []string{"c", "d"}}}, []interface{}{"a", "b", 1}, "d", false},
|
{map[string]map[string]interface{}{"a": {"b": []string{"c", "d"}}}, []interface{}{"a", "b", 1}, "d", false},
|
||||||
{map[string]map[string]string{"a": {"b": "c"}}, []interface{}{[]string{"a", "b"}}, "c", false},
|
{map[string]map[string]string{"a": {"b": "c"}}, []interface{}{[]string{"a", "b"}}, "c", false},
|
||||||
|
{maps.Params{"a": "av"}, []interface{}{"A"}, "av", false},
|
||||||
|
{maps.Params{"a": map[string]interface{}{"b": "bv"}}, []interface{}{"A", "B"}, "bv", false},
|
||||||
// errors
|
// errors
|
||||||
{nil, nil, nil, true},
|
{nil, nil, nil, true},
|
||||||
{[]int{0, 1}, []interface{}{"1"}, nil, true},
|
{[]int{0, 1}, []interface{}{"1"}, nil, true},
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
"github.com/gohugoio/hugo/tpl/compare"
|
"github.com/gohugoio/hugo/tpl/compare"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
)
|
)
|
||||||
|
@ -75,11 +76,19 @@ func (ns *Namespace) Sort(seq interface{}, args ...interface{}) (interface{}, er
|
||||||
} else {
|
} else {
|
||||||
v := p.Pairs[i].Value
|
v := p.Pairs[i].Value
|
||||||
var err error
|
var err error
|
||||||
for _, elemName := range path {
|
for i, elemName := range path {
|
||||||
v, err = evaluateSubElem(v, elemName)
|
v, err = evaluateSubElem(v, elemName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if !v.IsValid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Special handling of lower cased maps.
|
||||||
|
if params, ok := v.Interface().(maps.Params); ok {
|
||||||
|
v = reflect.ValueOf(params.Get(path[i+1:]...))
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.Pairs[i].Key = v
|
p.Pairs[i].Key = v
|
||||||
}
|
}
|
||||||
|
@ -89,6 +98,7 @@ func (ns *Namespace) Sort(seq interface{}, args ...interface{}) (interface{}, er
|
||||||
keys := seqv.MapKeys()
|
keys := seqv.MapKeys()
|
||||||
for i := 0; i < seqv.Len(); i++ {
|
for i := 0; i < seqv.Len(); i++ {
|
||||||
p.Pairs[i].Value = seqv.MapIndex(keys[i])
|
p.Pairs[i].Value = seqv.MapIndex(keys[i])
|
||||||
|
|
||||||
if sortByField == "" {
|
if sortByField == "" {
|
||||||
p.Pairs[i].Key = keys[i]
|
p.Pairs[i].Key = keys[i]
|
||||||
} else if sortByField == "value" {
|
} else if sortByField == "value" {
|
||||||
|
@ -96,11 +106,19 @@ func (ns *Namespace) Sort(seq interface{}, args ...interface{}) (interface{}, er
|
||||||
} else {
|
} else {
|
||||||
v := p.Pairs[i].Value
|
v := p.Pairs[i].Value
|
||||||
var err error
|
var err error
|
||||||
for _, elemName := range path {
|
for i, elemName := range path {
|
||||||
v, err = evaluateSubElem(v, elemName)
|
v, err = evaluateSubElem(v, elemName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if !v.IsValid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Special handling of lower cased maps.
|
||||||
|
if params, ok := v.Interface().(maps.Params); ok {
|
||||||
|
v = reflect.ValueOf(params.Get(path[i+1:]...))
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.Pairs[i].Key = v
|
p.Pairs[i].Key = v
|
||||||
}
|
}
|
||||||
|
@ -135,6 +153,7 @@ func (p pairList) Less(i, j int) bool {
|
||||||
// can only call Interface() on valid reflect Values
|
// can only call Interface() on valid reflect Values
|
||||||
return sortComp.Lt(iv.Interface(), jv.Interface())
|
return sortComp.Lt(iv.Interface(), jv.Interface())
|
||||||
}
|
}
|
||||||
|
|
||||||
// if j is invalid, test i against i's zero value
|
// if j is invalid, test i against i's zero value
|
||||||
return sortComp.Lt(iv.Interface(), reflect.Zero(iv.Type()))
|
return sortComp.Lt(iv.Interface(), reflect.Zero(iv.Type()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/deps"
|
"github.com/gohugoio/hugo/deps"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -100,6 +102,20 @@ func TestSort(t *testing.T) {
|
||||||
"asc",
|
"asc",
|
||||||
[]*TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}, {A: "g", B: "h"}, {A: "i", B: "j"}},
|
[]*TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}, {A: "g", B: "h"}, {A: "i", B: "j"}},
|
||||||
},
|
},
|
||||||
|
// Lower case Params, slice
|
||||||
|
{
|
||||||
|
[]TstParams{{params: maps.Params{"color": "indigo"}}, {params: maps.Params{"color": "blue"}}, {params: maps.Params{"color": "green"}}},
|
||||||
|
".Params.COLOR",
|
||||||
|
"asc",
|
||||||
|
[]TstParams{{params: maps.Params{"color": "blue"}}, {params: maps.Params{"color": "green"}}, {params: maps.Params{"color": "indigo"}}},
|
||||||
|
},
|
||||||
|
// Lower case Params, map
|
||||||
|
{
|
||||||
|
map[string]TstParams{"1": {params: maps.Params{"color": "indigo"}}, "2": {params: maps.Params{"color": "blue"}}, "3": {params: maps.Params{"color": "green"}}},
|
||||||
|
".Params.CoLoR",
|
||||||
|
"asc",
|
||||||
|
[]TstParams{{params: maps.Params{"color": "blue"}}, {params: maps.Params{"color": "green"}}, {params: maps.Params{"color": "indigo"}}},
|
||||||
|
},
|
||||||
// test map sorting by struct's method
|
// test map sorting by struct's method
|
||||||
{
|
{
|
||||||
map[string]TstX{"1": {A: "i", B: "j"}, "2": {A: "e", B: "f"}, "3": {A: "c", B: "d"}, "4": {A: "g", B: "h"}, "5": {A: "a", B: "b"}},
|
map[string]TstX{"1": {A: "i", B: "j"}, "2": {A: "e", B: "f"}, "3": {A: "c", B: "d"}, "4": {A: "g", B: "h"}, "5": {A: "a", B: "b"}},
|
||||||
|
|
|
@ -18,6 +18,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Where returns a filtered subset of a given data type.
|
// Where returns a filtered subset of a given data type.
|
||||||
|
@ -277,6 +279,7 @@ func evaluateSubElem(obj reflect.Value, elemName string) (reflect.Value, error)
|
||||||
if !obj.IsValid() {
|
if !obj.IsValid() {
|
||||||
return zero, errors.New("can't evaluate an invalid value")
|
return zero, errors.New("can't evaluate an invalid value")
|
||||||
}
|
}
|
||||||
|
|
||||||
typ := obj.Type()
|
typ := obj.Type()
|
||||||
obj, isNil := indirect(obj)
|
obj, isNil := indirect(obj)
|
||||||
|
|
||||||
|
@ -295,6 +298,7 @@ func evaluateSubElem(obj reflect.Value, elemName string) (reflect.Value, error)
|
||||||
if objPtr.Kind() != reflect.Interface && objPtr.CanAddr() {
|
if objPtr.Kind() != reflect.Interface && objPtr.CanAddr() {
|
||||||
objPtr = objPtr.Addr()
|
objPtr = objPtr.Addr()
|
||||||
}
|
}
|
||||||
|
|
||||||
mt, ok := objPtr.Type().MethodByName(elemName)
|
mt, ok := objPtr.Type().MethodByName(elemName)
|
||||||
if ok {
|
if ok {
|
||||||
switch {
|
switch {
|
||||||
|
@ -368,16 +372,22 @@ func parseWhereArgs(args ...interface{}) (mv reflect.Value, op string, err error
|
||||||
// Array or Slice.
|
// Array or Slice.
|
||||||
func (ns *Namespace) checkWhereArray(seqv, kv, mv reflect.Value, path []string, op string) (interface{}, error) {
|
func (ns *Namespace) checkWhereArray(seqv, kv, mv reflect.Value, path []string, op string) (interface{}, error) {
|
||||||
rv := reflect.MakeSlice(seqv.Type(), 0, 0)
|
rv := reflect.MakeSlice(seqv.Type(), 0, 0)
|
||||||
|
|
||||||
for i := 0; i < seqv.Len(); i++ {
|
for i := 0; i < seqv.Len(); i++ {
|
||||||
var vvv reflect.Value
|
var vvv reflect.Value
|
||||||
rvv := seqv.Index(i)
|
rvv := seqv.Index(i)
|
||||||
|
|
||||||
if kv.Kind() == reflect.String {
|
if kv.Kind() == reflect.String {
|
||||||
vvv = rvv
|
if params, ok := rvv.Interface().(maps.Params); ok {
|
||||||
for _, elemName := range path {
|
vvv = reflect.ValueOf(params.Get(path...))
|
||||||
var err error
|
} else {
|
||||||
vvv, err = evaluateSubElem(vvv, elemName)
|
vvv = rvv
|
||||||
if err != nil {
|
for _, elemName := range path {
|
||||||
continue
|
var err error
|
||||||
|
vvv, err = evaluateSubElem(vvv, elemName)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,9 +16,12 @@ package collections
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/deps"
|
"github.com/gohugoio/hugo/deps"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -162,6 +165,37 @@ func TestWhere(t *testing.T) {
|
||||||
{1: "a", 2: "m"},
|
{1: "a", 2: "m"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
seq: []maps.Params{
|
||||||
|
{"a": "a1", "b": "b1"}, {"a": "a2", "b": "b2"},
|
||||||
|
},
|
||||||
|
key: "B", match: "b2",
|
||||||
|
expect: []maps.Params{
|
||||||
|
maps.Params{"a": "a2", "b": "b2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
seq: []maps.Params{
|
||||||
|
maps.Params{
|
||||||
|
"a": map[string]interface{}{
|
||||||
|
"b": "b1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
maps.Params{
|
||||||
|
"a": map[string]interface{}{
|
||||||
|
"b": "b2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
key: "A.B", match: "b2",
|
||||||
|
expect: []maps.Params{
|
||||||
|
maps.Params{
|
||||||
|
"a": map[string]interface{}{
|
||||||
|
"b": "b2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
seq: []*TstX{
|
seq: []*TstX{
|
||||||
{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
|
{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
|
||||||
|
@ -557,11 +591,24 @@ func TestWhere(t *testing.T) {
|
||||||
"zap": []interface{}{map[interface{}]interface{}{"a": 5, "b": 6}},
|
"zap": []interface{}{map[interface{}]interface{}{"a": 5, "b": 6}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
seq: map[string]interface{}{
|
||||||
|
"foo": []interface{}{maps.Params{"a": 1, "b": 2}},
|
||||||
|
"bar": []interface{}{maps.Params{"a": 3, "b": 4}},
|
||||||
|
"zap": []interface{}{maps.Params{"a": 5, "b": 6}},
|
||||||
|
},
|
||||||
|
key: "B", op: ">", match: 3,
|
||||||
|
expect: map[string]interface{}{
|
||||||
|
"bar": []interface{}{maps.Params{"a": 3, "b": 4}},
|
||||||
|
"zap": []interface{}{maps.Params{"a": 5, "b": 6}},
|
||||||
|
},
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
|
|
||||||
testVariants := createTestVariants(test)
|
testVariants := createTestVariants(test)
|
||||||
for j, test := range testVariants {
|
for j, test := range testVariants {
|
||||||
name := fmt.Sprintf("[%d/%d] %T %s %s", i, j, test.seq, test.op, test.key)
|
name := fmt.Sprintf("%d/%d %T %s %s", i, j, test.seq, test.op, test.key)
|
||||||
|
name = strings.ReplaceAll(name, "[]", "slice-of-")
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
var results interface{}
|
var results interface{}
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -19,11 +19,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
_errors "github.com/pkg/errors"
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/deps"
|
"github.com/gohugoio/hugo/deps"
|
||||||
"github.com/gohugoio/hugo/resources"
|
"github.com/gohugoio/hugo/resources"
|
||||||
"github.com/gohugoio/hugo/resources/resource"
|
"github.com/gohugoio/hugo/resources/resource"
|
||||||
|
_errors "github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/resources/resource_factories/bundler"
|
"github.com/gohugoio/hugo/resources/resource_factories/bundler"
|
||||||
"github.com/gohugoio/hugo/resources/resource_factories/create"
|
"github.com/gohugoio/hugo/resources/resource_factories/create"
|
||||||
|
@ -301,7 +301,7 @@ func (ns *Namespace) resolveArgs(args []interface{}) (resources.ResourceTransfor
|
||||||
return nil, nil, fmt.Errorf("type %T not supported in Resource transformations", args[0])
|
return nil, nil, fmt.Errorf("type %T not supported in Resource transformations", args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
m, err := cast.ToStringMapE(args[0])
|
m, err := maps.ToStringMapE(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, _errors.Wrap(err, "invalid options type")
|
return nil, nil, _errors.Wrap(err, "invalid options type")
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,10 @@ import (
|
||||||
texttemplate "text/template"
|
texttemplate "text/template"
|
||||||
"text/template/parse"
|
"text/template/parse"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/tpl"
|
"github.com/gohugoio/hugo/tpl"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/spf13/cast"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// decl keeps track of the variable mappings, i.e. $mysite => .Site etc.
|
// decl keeps track of the variable mappings, i.e. $mysite => .Site etc.
|
||||||
|
@ -315,7 +314,7 @@ func (c *templateContext) collectConfig(n *parse.PipeNode) {
|
||||||
|
|
||||||
if s, ok := cmd.Args[0].(*parse.StringNode); ok {
|
if s, ok := cmd.Args[0].(*parse.StringNode); ok {
|
||||||
errMsg := "failed to decode $_hugo_config in template"
|
errMsg := "failed to decode $_hugo_config in template"
|
||||||
m, err := cast.ToStringMapE(s.Text)
|
m, err := maps.ToStringMapE(s.Text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.err = errors.Wrap(err, errMsg)
|
c.err = errors.Wrap(err, errMsg)
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in a new issue