From 3400aff2588cbf9dd4629c05537d16b019d0fdf5 Mon Sep 17 00:00:00 2001 From: Gareth Watts Date: Thu, 22 Oct 2020 12:14:14 -0500 Subject: [PATCH] Allow cascade _target to work with non toml fm The TOML lib unmarshals slices of string maps to []map[string]interface{} whereas YAML and JSON decode to []interface{} The existing tests only check for TOML working correctly, and _target with cascade did not work at all for frontmatter defined in other formats. Add a function to normalize those slices Fixes #7874 --- common/maps/maps.go | 18 ++++++++++++++ common/maps/maps_test.go | 33 ++++++++++++++++++++++++ hugolib/cascade_test.go | 54 ++++++++++++++++++++++++++++++++++++++++ hugolib/page__meta.go | 7 +++--- 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/common/maps/maps.go b/common/maps/maps.go index 8b42ca764..41d9b6e15 100644 --- a/common/maps/maps.go +++ b/common/maps/maps.go @@ -14,6 +14,7 @@ package maps import ( + "fmt" "strings" "github.com/gobwas/glob" @@ -64,6 +65,23 @@ func ToStringMap(in interface{}) map[string]interface{} { return m } +func ToSliceStringMap(in interface{}) ([]map[string]interface{}, error) { + switch v := in.(type) { + case []map[string]interface{}: + return v, nil + case []interface{}: + var s []map[string]interface{} + for _, entry := range v { + if vv, ok := entry.(map[string]interface{}); ok { + s = append(s, vv) + } + } + return s, nil + default: + return nil, fmt.Errorf("unable to cast %#v of type %T to []map[string]interface{}", in, in) + } +} + type keyRename struct { pattern glob.Glob newKey string diff --git a/common/maps/maps_test.go b/common/maps/maps_test.go index 6e4947adb..bde77071d 100644 --- a/common/maps/maps_test.go +++ b/common/maps/maps_test.go @@ -75,6 +75,39 @@ func TestToLower(t *testing.T) { } } +func TestToSliceStringMap(t *testing.T) { + c := qt.New(t) + + tests := []struct { + input interface{} + expected []map[string]interface{} + }{ + { + input: []map[string]interface{}{ + {"abc": 123}, + }, + expected: []map[string]interface{}{ + {"abc": 123}, + }, + }, { + input: []interface{}{ + map[string]interface{}{ + "def": 456, + }, + }, + expected: []map[string]interface{}{ + {"def": 456}, + }, + }, + } + + for _, test := range tests { + v, err := ToSliceStringMap(test.input) + c.Assert(err, qt.IsNil) + c.Assert(v, qt.DeepEquals, test.expected) + } +} + func TestRenameKeys(t *testing.T) { c := qt.New(t) diff --git a/hugolib/cascade_test.go b/hugolib/cascade_test.go index 336acdcf3..a112fe10c 100644 --- a/hugolib/cascade_test.go +++ b/hugolib/cascade_test.go @@ -459,4 +459,58 @@ S1|p1:|p2:p2| }) + c.Run("slice with yaml _target", func(c *qt.C) { + b := newBuilder(c) + + b.WithContent("_index.md", `--- +title: "Home" +cascade: +- p1: p1 + _target: + path: "**p1**" +- p2: p2 + _target: + kind: "section" +--- +`) + + b.Build(BuildCfg{}) + + b.AssertFileContent("public/index.html", ` +P1|p1:p1|p2:| +S1|p1:|p2:p2| +`) + + }) + + c.Run("slice with json _target", func(c *qt.C) { + b := newBuilder(c) + + b.WithContent("_index.md", `{ +"title": "Home", +"cascade": [ + { + "p1": "p1", + "_target": { + "path": "**p1**" + } + },{ + "p2": "p2", + "_target": { + "kind": "section" + } + } +] +} +`) + + b.Build(BuildCfg{}) + + b.AssertFileContent("public/index.html", ` + P1|p1:p1|p2:| + S1|p1:|p2:p2| + `) + + }) + } diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go index 52ffbb880..d23718315 100644 --- a/hugolib/page__meta.go +++ b/hugolib/page__meta.go @@ -342,8 +342,7 @@ func (pm *pageMeta) setMetadata(parentBucket *pagesMapBucket, p *pageState, fron if p.bucket != nil { // Check for any cascade define on itself. if cv, found := frontmatter["cascade"]; found { - switch v := cv.(type) { - case []map[string]interface{}: + if v, err := maps.ToSliceStringMap(cv); err == nil { p.bucket.cascade = make(map[page.PageMatcher]maps.Params) for _, vv := range v { @@ -367,12 +366,12 @@ func (pm *pageMeta) setMetadata(parentBucket *pagesMapBucket, p *pageState, fron } } - default: + } else { p.bucket.cascade = map[page.PageMatcher]maps.Params{ page.PageMatcher{}: maps.ToStringMap(cv), } - } + } } } } else {