Allow cascade _target to work with non toml fm
authorGareth Watts <gareth@omnipotent.net>
Thu, 22 Oct 2020 17:14:14 +0000 (12:14 -0500)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Thu, 22 Oct 2020 21:00:19 +0000 (23:00 +0200)
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
common/maps/maps_test.go
hugolib/cascade_test.go
hugolib/page__meta.go

index 8b42ca7646dded3d36d6e0ce3c340d9993f42b36..41d9b6e15b88449c52785c1e0b8a8777721fda8c 100644 (file)
@@ -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
index 6e4947adb2b9d535e79abd73b9eade36bacfe78a..bde77071de8cde8dd4428c09056f7c32e66a760d 100644 (file)
@@ -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)
 
index 336acdcf354bd98a4cd1288fd961f368911f2a20..a112fe10c06b2928a826c8a1c63d9312649406bf 100644 (file)
@@ -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|
+               `)
+
+       })
+
 }
index 52ffbb88051c67abf32207aa9f0b7a44c1883258..d23718315fda670fe5888f48ae4b553269ca6ee9 100644 (file)
@@ -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 {